blob: fc4d65b83a867de33050147aba7f96d83bbc1aa5 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.oozie.action.hadoop;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.net.ConnectException;
import java.net.UnknownHostException;
import java.net.URISyntaxException;
import java.util.List;
import com.google.common.base.Preconditions;
import org.apache.hadoop.util.DiskChecker;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsAction;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.fs.permission.AclEntry;
import org.apache.hadoop.fs.permission.AclEntryType;
import org.apache.hadoop.security.AccessControlException;
import org.apache.oozie.action.ActionExecutorException;
import org.apache.oozie.action.ActionExecutorException.ErrorType;
import org.apache.oozie.service.HadoopAccessorException;
import org.apache.oozie.util.XLog;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
public class GitActionExecutor extends JavaActionExecutor {
private static final String GIT_MAIN_CLASS_NAME =
"org.apache.oozie.action.hadoop.GitMain";
public static final String GIT_ACTION_TYPE = "git";
static final String APP_NAME = "oozie.app.name";
static final String WORKFLOW_ID = "oozie.workflow.id";
static final String CALLBACK_URL = "oozie.callback.url";
static final String RESOURCE_MANAGER = "oozie.resource.manager";
static final String NAME_NODE = "oozie.name.node";
static final String GIT_URI = "oozie.git.source.uri";
static final String GIT_BRANCH = "oozie.git.branch";
static final String DESTINATION_URI = "oozie.git.destination.uri";
static final String KEY_PATH = "oozie.git.key.path";
static final String ACTION_TYPE = "oozie.action.type";
static final String ACTION_NAME = "oozie.action.name";
public GitActionExecutor() {
super(GIT_ACTION_TYPE);
}
@Override
public List<Class<?>> getLauncherClasses() {
List<Class<?>> classes = new ArrayList<Class<?>>();
try {
classes.add(Class.forName(GIT_MAIN_CLASS_NAME));
}
catch (ClassNotFoundException e) {
throw new RuntimeException("Class not found", e);
}
return classes;
}
@Override
protected String getLauncherMain(Configuration launcherConf, Element actionXml) {
return launcherConf.get(LauncherAMUtils.CONF_OOZIE_ACTION_MAIN_CLASS,
GIT_MAIN_CLASS_NAME);
}
@Override
public void initActionType() {
super.initActionType();
registerErrors();
}
private void registerErrors() {
registerError(UnknownHostException.class.getName(), ErrorType.TRANSIENT, "GIT001");
registerError(AccessControlException.class.getName(), ErrorType.NON_TRANSIENT,
"GIT002");
registerError(DiskChecker.DiskOutOfSpaceException.class.getName(),
ErrorType.NON_TRANSIENT, "GIT003");
registerError(org.apache.hadoop.hdfs.protocol.QuotaExceededException.class.getName(),
ErrorType.NON_TRANSIENT, "GIT004");
registerError(org.apache.hadoop.hdfs.server.namenode.SafeModeException.class.getName(),
ErrorType.NON_TRANSIENT, "GIT005");
registerError(ConnectException.class.getName(), ErrorType.TRANSIENT, "GIT006");
registerError(JDOMException.class.getName(), ErrorType.ERROR, "GIT007");
registerError(FileNotFoundException.class.getName(), ErrorType.ERROR, "GIT008");
registerError(IOException.class.getName(), ErrorType.TRANSIENT, "GIT009");
registerError(NullPointerException.class.getName(), ErrorType.ERROR, "GIT010");
}
@Override
Configuration setupActionConf(Configuration actionConf, Context context,
Element actionXml, Path appPath) throws ActionExecutorException {
super.setupActionConf(actionConf, context, actionXml, appPath);
Namespace ns = actionXml.getNamespace();
ActionConfVerifier confChecker = new ActionConfVerifier(actionConf);
confChecker.checkTrimAndSet(GitActionExecutor.NAME_NODE, actionXml.getChild("name-node", ns));
confChecker.checkTrimAndSet(GitActionExecutor.DESTINATION_URI,
actionXml.getChild("destination-uri", ns));
confChecker.checkTrimAndSet(GitActionExecutor.GIT_URI, actionXml.getChild("git-uri", ns));
confChecker.trimAndSet(KEY_PATH, actionXml.getChild("key-path", ns));
String keyPath = actionConf.get(KEY_PATH);
if (keyPath != null && keyPath.length() > 0) {
try {
confChecker.verifyKeyPermissions(context.getAppFileSystem(), new Path(keyPath));
} catch (HadoopAccessorException|URISyntaxException|IOException e){
throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "GIT011", XLog
.format("Not able to verify permissions on key file {0}", keyPath), e);
}
}
confChecker.trimAndSet(GIT_BRANCH, actionXml.getChild("branch", ns));
actionConf.set(ACTION_TYPE, getType());
actionConf.set(ACTION_NAME, GIT_ACTION_TYPE);
return actionConf;
}
@Override
protected String getDefaultShareLibName(Element actionXml) {
return GIT_ACTION_TYPE;
}
static class ActionConfVerifier {
private final Configuration actionConf;
/**
* Create ActionConfVerifier checker which will set action conf values and throw
* ActionExecutorException's with the exception code provided
* @param actionConf the actionConf in which to set values
*
*/
ActionConfVerifier(Configuration actionConf) {
this.actionConf = actionConf;
}
/**
* Validates Git key permissions are secure on disk and throw an exception if not.
* Otherwise exit out gracefully
*/
void verifyKeyPermissions(FileSystem fs, Path keyPath) throws IOException, ActionExecutorException{
String failedPermsWarning = "The permissions on the access key {0} are considered insecure: {1}";
FileStatus status = fs.getFileStatus(keyPath);
FsPermission perms = status.getPermission();
// check standard permissioning for other's read access
if (perms.getOtherAction().and(FsAction.READ) == FsAction.READ) {
throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "GIT012", XLog
.format(failedPermsWarning, keyPath, perms.toString()));
}
// check if any ACLs have been specified which allow others read access
if (perms.getAclBit()) {
List<AclEntry> aclEntries = new ArrayList<>(fs.getAclStatus(keyPath).getEntries());
for (AclEntry acl: aclEntries) {
if (acl.getType() == AclEntryType.OTHER && acl.getPermission().and(FsAction.READ) == FsAction.READ) {
throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "GIT013", XLog
.format(failedPermsWarning, keyPath, perms.toString()));
}
}
}
}
/**
* Calls helper function to verify value not null and throw an exception if so.
* Otherwise, set actionConf value displayName to XML trimmed text value
*/
void checkTrimAndSet(String displayName, Element value) {
Preconditions.checkNotNull(value, "Action Configuration does not have [%s] property", displayName);
actionConf.set(displayName, value.getTextTrim());
}
/**
* Calls helper function to verify value not null and throw an exception if so.
* Otherwise, set actionConf value displayName to value
*/
void checkAndSet(String displayName, String value) {
Preconditions.checkNotNull(value, "Action Configuration does not have [%s] property", displayName);
actionConf.set(displayName, value);
}
/**
* *f value is null, does nothing
* If value is not null, sets actionConf value displayName to XML trimmed text value
*/
void trimAndSet(String displayName, Element value) {
if (value != null) {
actionConf.set(displayName, value.getTextTrim());
}
}
/**
* Calls helper function to verify name not null and throw an exception if so.
* Otherwise, returns actionConf value
* @param name - actionConf value to return
*/
String checkAndGetTrimmed(String name) {
Preconditions.checkNotNull(actionConf.getTrimmed(name), "Action Configuration does not have [%s] property", name);
return actionConf.getTrimmed(name);
}
}
}