Package com.dtolabs.rundeck.core.common

Source Code of com.dtolabs.rundeck.core.common.FrameworkProject

/*
* Copyright 2010 DTO Labs, Inc. (http://dtolabs.com)
*
*  Licensed 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 com.dtolabs.rundeck.core.common;

import com.dtolabs.rundeck.core.authorization.Attribute;
import com.dtolabs.rundeck.core.authorization.providers.EnvironmentalContext;
import com.dtolabs.rundeck.core.common.impl.URLFileUpdater;
import com.dtolabs.rundeck.core.execution.service.ExecutionServiceException;
import com.dtolabs.rundeck.core.plugins.configuration.Describable;
import com.dtolabs.rundeck.core.plugins.configuration.Description;
import com.dtolabs.rundeck.core.resources.*;
import com.dtolabs.rundeck.core.resources.format.*;
import com.dtolabs.rundeck.core.utils.PropertyLookup;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
* Represents a project in the framework. A project is a repository of installed managed entities
* organized by their type.
* <p/>
*/
public class FrameworkProject extends FrameworkResourceParent {
    public static final String PROP_FILENAME = "project.properties";
    public static final String ETC_DIR_NAME = "etc";
    public static final String NODES_XML = "resources.xml";
    public static final String PROJECT_RESOURCES_URL_PROPERTY = "project.resources.url";
    public static final String PROJECT_RESOURCES_FILE_PROPERTY = "project.resources.file";
    public static final String PROJECT_RESOURCES_FILEFORMAT_PROPERTY = "project.resources.file.format";
    public static final String PROJECT_RESOURCES_ALLOWED_URL_PREFIX = "project.resources.allowedURL.";
    public static final String FRAMEWORK_RESOURCES_ALLOWED_URL_PREFIX = "framework.resources.allowedURL.";
    public static final String RESOURCES_SOURCE_PROP_PREFIX = "resources.source";
    public static final String PROJECT_RESOURCES_MERGE_NODE_ATTRIBUTES = "project.resources.mergeNodeAttributes";

    /**
     * Creates an authorization environment for a project.
     * @param project project name
     * @return environment to evaluate authorization for a project
     */
    public static Set<Attribute> authorizationEnvironment(final String project) {
        return Collections.singleton(new Attribute(URI.create(EnvironmentalContext.URI_BASE + "project"),
                project));
    }
    /**
     * Reference to deployments base directory
     */
    private final File resourcesBaseDir;

    /**
     * Reference to the project's config dir
     */
    private final File etcDir;

    private final IFrameworkProjectMgr projectResourceMgr;

    private final File propertyFile;

    /**
     * reference to PropertyLookup object providing access to project.properties
     */
    private PropertyLookup lookup;
    private List<ResourceModelSource> nodesSourceList;

    /**
     * Constructor
     *
     * @param name    Name of the project
     * @param basedir the base directory for the Depot
     */
    public FrameworkProject(final String name, final File basedir, final IFrameworkProjectMgr resourceMgr) {
        this(name, basedir, resourceMgr, null);
    }
    /**
     * Constructor
     *
     * @param name    Name of the project
     * @param basedir the base directory for the Depot
     */
    public FrameworkProject(final String name, final File basedir, final IFrameworkProjectMgr resourceMgr, final Properties properties) {
        super(name, basedir, resourceMgr);
        projectResourceMgr = resourceMgr;
        resourcesBaseDir = new File(getBaseDir(), "resources");
        etcDir = getProjectEtcDir(getBaseDir());
        if (!etcDir.exists()) {
            if (!etcDir.mkdirs()) {
                throw new FrameworkResourceException("error while creating project structure. " +
                        "failed creating directory: " + etcDir.getAbsolutePath(), this );
            }
        }

        propertyFile = getProjectPropertyFile(getBaseDir());
        if ( !propertyFile.exists()) {
            generateProjectPropertiesFile(false, properties, true);
        }
        loadProperties();

        nodesSourceList = new ArrayList<ResourceModelSource>();

        initialize();
    }

    /**
     * Get the etc dir from the basedir
     * @param baseDir
     * @return
     */
    private static File getProjectEtcDir(File baseDir) {
        return new File(baseDir, ETC_DIR_NAME);
    }

    /**
     * Get the project property file from the basedir
     * @param baseDir
     * @return
     */
    private static File getProjectPropertyFile(File baseDir) {
        return new File(getProjectEtcDir(baseDir), PROP_FILENAME);
    }

    private long propertiesLastReload=0L;
    private synchronized void checkReloadProperties(){
        if (needsPropertiesReload()) {
            loadProperties();
        }
    }

    private boolean needsPropertiesReload(){
        final File fwkProjectPropertyFile = new File(projectResourceMgr.getFramework().getConfigDir(), PROP_FILENAME);
        final long fwkPropsLastModified = fwkProjectPropertyFile.lastModified();
        if(propertyFile.exists()){
            return propertyFile.lastModified()>propertiesLastReload || fwkPropsLastModified>propertiesLastReload;
        }else{
            return fwkPropsLastModified > propertiesLastReload;
        }
    }
    private synchronized void loadProperties() {
        //generic framework properties for a project
        final File fwkProjectPropertyFile = new File(projectResourceMgr.getFramework().getConfigDir(), PROP_FILENAME);

        lookup = createProjectPropertyLookup(
                projectResourceMgr.getFramework().getBaseDir(),
                projectResourceMgr.getFramework().getFrameworkProjectsBaseDir(),
                getName()
        );

        if (propertyFile.exists()) {
            getLogger().debug("loading existing project.properties: " + propertyFile.getAbsolutePath());
            final long fwkPropsLastModified = fwkProjectPropertyFile.lastModified();
            final long propsLastMod = propertyFile.lastModified();
            propertiesLastReload = propsLastMod > fwkPropsLastModified ? propsLastMod : fwkPropsLastModified;
        } else {
            getLogger().debug("loading instance-level project.properties: " + propertyFile.getAbsolutePath());
            propertiesLastReload = fwkProjectPropertyFile.lastModified();
        }
    }

    /**
     * Create PropertyLookup for a project from the framework basedir
     *
     * @param baseDir the framework basedir
     * @param projectsBaseDir
     * @param projectName
     * @return
     */
    private static PropertyLookup createProjectPropertyLookup(File baseDir, File projectsBaseDir, String projectName) {
        PropertyLookup lookup;
        final Properties ownProps = new Properties();
        ownProps.setProperty("project.name", projectName);

        //generic framework properties for a project
        final File fwkProjectPropertyFile = Framework.getPropertyFile(Framework.getConfigDir(baseDir));
        final Properties nodeWideDepotProps = PropertyLookup.fetchProperties(fwkProjectPropertyFile);
        nodeWideDepotProps.putAll(ownProps);

        final File propertyFile = getProjectPropertyFile(new File(projectsBaseDir, projectName));

        if (propertyFile.exists()) {
            lookup = PropertyLookup.create(propertyFile,
                    nodeWideDepotProps, Framework.createPropertyLookupFromBasedir(baseDir));
        } else {
            lookup = PropertyLookup.create(fwkProjectPropertyFile,
                    ownProps, Framework.createPropertyLookupFromBasedir(baseDir));
        }
        lookup.expand();
        return lookup;
    }

    /**
     * Create a property retriever for a project given the framework basedir
     *
     * @param baseDir the framework basedir
     * @param projectsBaseDir
     * @param projectName
     * @return
     */
    public static PropertyRetriever createProjectPropertyRetriever(File baseDir, File projectsBaseDir, String projectName) {
        return createProjectPropertyLookup(baseDir, projectsBaseDir, projectName).safe();
    }

    private ArrayList<Exception> nodesSourceExceptions;
    private long nodesSourcesLastReload = 0L;
    private void loadResourceModelSources() {
        nodesSourceExceptions = new ArrayList<Exception>();
        //generate Configuration for file source
        if (hasProperty(PROJECT_RESOURCES_FILE_PROPERTY)) {
            try {
                final Properties config = createFileSourceConfiguration();
                logger.info("Source (project.resources.file): loading with properties: " + config);
                nodesSourceList.add(loadResourceModelSource("file", config, shouldCacheForType("file"),"file.file"));
            } catch (ExecutionServiceException e) {
                logger.error("Failed to load file resource model source: " + e.getMessage(), e);
                nodesSourceExceptions.add(e);
            }
        }
        if(hasProperty(PROJECT_RESOURCES_URL_PROPERTY)) {
            try{
                final Properties config = createURLSourceConfiguration();
                logger.info("Source (project.resources.url): loading with properties: " + config);
                nodesSourceList.add(loadResourceModelSource("url", config, shouldCacheForType("url"), "file.url"));
            } catch (ExecutionServiceException e) {
                logger.error("Failed to load file resource model source: " + e.getMessage(), e);
                nodesSourceExceptions.add(e);
            }
        }

        final List<Map> list = listResourceModelConfigurations();
        int i=1;
        for (final Map map : list) {
            final String providerType = (String) map.get("type");
            final Properties props = (Properties) map.get("props");

            logger.info("Source #" + i + " (" + providerType + "): loading with properties: " + props);
            try {
                nodesSourceList.add(loadResourceModelSource(providerType, props, shouldCacheForType(providerType),
                        i + "." + providerType));
            } catch (ExecutionServiceException e) {
                logger.error("Failed loading resource model source #" + i + ", skipping: " + e.getMessage(), e);
                nodesSourceExceptions.add(e);
            }
            i++;
        }

        nodesSourcesLastReload = getPropertyFile().lastModified();
    }

    static Set<String> uncachedResourceTypes = new HashSet<String>();
    static {
        uncachedResourceTypes.add("file");
        uncachedResourceTypes.add("directory");
    }
    private boolean shouldCacheForType(String type) {
        return !uncachedResourceTypes.contains(type);
    }

    /**
     * list the configurations of resource model providers.  Returns a list of maps containing:
     * <li>type - provider type name</li>
     * <li>props - configuration properties</li>
     */
    public synchronized List<Map> listResourceModelConfigurations(){
        final ArrayList<Map> list = new ArrayList<Map>();
        int i = 1;
        boolean done = false;
        while (!done) {
            final String prefix = RESOURCES_SOURCE_PROP_PREFIX + "." + i;
            if (hasProperty(prefix + ".type")) {
                final String providerType = getProperty(prefix + ".type");
                final Properties props = new Properties();
                props.setProperty("project", getName());
                final int len = (prefix + ".config.").length();
                for (final Object o : lookup.getPropertiesMap().keySet()) {
                    final String key = (String) o;
                    if (key.startsWith(prefix + ".config.")) {
                        props.setProperty(key.substring(len), getProperty(key));
                    }
                }
                logger.info("Source #" + i + " (" + providerType + "): loading with properties: " + props);
                final HashMap<String, Object> map = new HashMap<String, Object>();
                map.put("type", providerType);
                map.put("props", props);
                list.add(map);
            } else {
                done = true;
            }
            i++;
        }
        return list;
    }

    private Properties createURLSourceConfiguration() {
        final URLResourceModelSource.Configuration build = URLResourceModelSource.Configuration.build();
        build.url(getProperty(PROJECT_RESOURCES_URL_PROPERTY));
        build.project(getName());

        return build.getProperties();
    }

    private synchronized Collection<ResourceModelSource> getResourceModelSources() {
        //determine if sources need to be reloaded
        final long lastMod = getPropertyFile().lastModified();
        if(lastMod> nodesSourcesLastReload){
            nodesSourceList = new ArrayList<ResourceModelSource>();
            loadResourceModelSources();
        }
        return nodesSourceList;
    }

    private ResourceModelSource loadResourceModelSource(String type, Properties configuration, boolean useCache,
            String ident) throws ExecutionServiceException {

        final ResourceModelSourceService nodesSourceService =
                getFrameworkProjectMgr().getFramework().getResourceModelSourceService();
        ResourceModelSource sourceForConfiguration = nodesSourceService.getSourceForConfiguration(type, configuration);

        if (useCache) {
            ResourceModelSourceFactory provider = nodesSourceService.providerOfType(type);
            String name=ident;
            if(provider instanceof Describable){
                Describable desc=(Describable) provider;
                Description description = desc.getDescription();
                name = ident + " (" + description.getTitle() + ")";
            }
            return createCachingSource(sourceForConfiguration, ident,name);
        } else {
            return sourceForConfiguration;
        }
    }

    private ResourceModelSource createCachingSource(ResourceModelSource sourceForConfiguration, String ident, String descr) {
        final File file = getResourceModelSourceFileCacheForType(ident);
        final Framework framework = getFrameworkProjectMgr().getFramework();
        final ResourceModelSourceService nodesSourceService = framework.getResourceModelSourceService();
        final ResourceFormatGeneratorService resourceFormatGeneratorService = framework.getResourceFormatGeneratorService();
        final Properties fileSourceConfig = generateFileSourceConfigurationProperties(file.getAbsolutePath(),
                ResourceXMLFormatGenerator.SERVICE_PROVIDER_TYPE, true, false);
        try {
            ResourceModelSource fileSource = nodesSourceService.getSourceForConfiguration("file", fileSourceConfig);

            ResourceFormatGenerator generatorForFormat = resourceFormatGeneratorService.getGeneratorForFormat
                    (ResourceXMLFormatGenerator.SERVICE_PROVIDER_TYPE);

            String ident1 = "[ResourceModelSource: " + descr + ", project: " + getName() + "]";
            return new CachingResourceModelSource(
                    sourceForConfiguration,
                    ident1,
                    new LoggingResourceModelSourceCache(
                            new FileResourceModelSourceCache(file, generatorForFormat, fileSource),
                            ident1));
        } catch (UnsupportedFormatException e) {
            e.printStackTrace();
        } catch (ExecutionServiceException e) {
            e.printStackTrace();
        }
        return null;
    }

    private File getResourceModelSourceFileCacheForType(String ident) {
        String varDir = getProperty("framework.var.dir");
        File file = new File(varDir, "resourceModelSourceCache/" + getName() + "/" + ident + ".xml");
        if(!file.getParentFile().exists() && !file.getParentFile().mkdirs()){
            logger.warn("Failed to create cache dirs for source file cache");
        }
        return file;
    }

    private Properties createFileSourceConfiguration() {
        String format=null;
        if(hasProperty(PROJECT_RESOURCES_FILEFORMAT_PROPERTY)) {
            format = getProperty(PROJECT_RESOURCES_FILEFORMAT_PROPERTY);
        }
        return generateFileSourceConfigurationProperties(getProperty(PROJECT_RESOURCES_FILE_PROPERTY), format, true,
                true);
    }

    private Properties generateFileSourceConfigurationProperties(String filepath, String format, boolean generate,
            boolean includeServerNode) {
        final FileResourceModelSource.Configuration build = FileResourceModelSource.Configuration.build();
        build.file(filepath);
        if(null!=format){
            build.format(format);
        }
        build.project(getName());
        build.generateFileAutomatically(generate);
        build.includeServerNode(includeServerNode);

        return build.getProperties();
    }


    /**
     * Create a new Depot object at the specified projects.directory
     * @param name
     * @param projectsDir
     * @param resourceMgr
     * @return
     */
    public static FrameworkProject create(final String name, final File projectsDir, final IFrameworkProjectMgr resourceMgr) {
        return new FrameworkProject(name, new File(projectsDir, name), resourceMgr);
    }
    /**
     * Create a new Depot object at the specified projects.directory
     * @param name
     * @param projectsDir
     * @param resourceMgr
     * @return
     */
    public static FrameworkProject create(final String name, final File projectsDir, final IFrameworkProjectMgr resourceMgr, final Properties properties) {
        return new FrameworkProject(name, new File(projectsDir, name), resourceMgr, properties);
    }


    public IFrameworkResource loadChild(String name) {
        throw new NoSuchResourceException("project named " + name + " doesn't exist", this);
    }

    public boolean childCouldBeLoaded(String name) {

        final File file = new File(resourcesBaseDir, name);
        return file.exists() && file.isDirectory();
    }

    public Collection listChildNames() {
        HashSet childnames = new HashSet();
        if(resourcesBaseDir.isDirectory()){
            final String[] list = resourcesBaseDir.list();
            if (null != list) {
                for (int i = 0; i < list.length; i++) {
                    final File dir = new File(resourcesBaseDir, list[i]);
                    if (dir.isDirectory()) {
                        childnames.add(dir.getName());
                    }
                }
            }
        }
        return childnames;
    }

    /**
     * Create a new type and store it
     *
     * @param resourceType
     * @return
     */
    public IFrameworkResource createChild(final String resourceType) {
        throw new UnsupportedOperationException("createChild");
    }


    public File getPropertyFile() {
        return propertyFile;
    }

    public IFrameworkProjectMgr getFrameworkProjectMgr() {
        return projectResourceMgr;
    }

    public static boolean exists(final String project, final IFrameworkProjectMgr projectResourceMgr) {
        return projectResourceMgr.existsFrameworkProject(project);
    }

    public boolean existsFrameworkType(final String name) {
        return existsChild(name);
    }

    /**
     * Gets the config dir for this project
     *
     * @return etc directory
     */
    public File getEtcDir() {
        return etcDir;
    }


    /**
     * Return specific nodes resources file path for the project, based on the framework.nodes.file.name property
     * @return
     */
    public String getNodesResourceFilePath() {
        if(hasProperty(PROJECT_RESOURCES_FILE_PROPERTY)) {
            return new File(getProperty(PROJECT_RESOURCES_FILE_PROPERTY)).getAbsolutePath();
        }
        final Framework framework = projectResourceMgr.getFramework();
        final String s;
        if(framework.hasProperty(Framework.NODES_RESOURCES_FILE_PROP)){
            return new File(getEtcDir(), framework.getProperty(Framework.NODES_RESOURCES_FILE_PROP)).getAbsolutePath();
        }else{
            return new File(getEtcDir(), NODES_XML).getAbsolutePath();
        }
    }
    /**
     * Returns the set of nodes for the project
     *
     * @return an instance of {@link INodeSet}
     */
    public INodeSet getNodeSet() throws NodeFileParserException {
        //iterate through sources, and add nodes
        final NodeSetMerge list = getNodeSetMerge();
        nodesSourceExceptions = new ArrayList<Exception>();
        for (final ResourceModelSource nodesSource : getResourceModelSources()) {
            try {
                INodeSet nodes = nodesSource.getNodes();
                if (null == nodes) {
                    logger.warn("Empty nodes result from [" + nodesSource.toString() + "]");
                } else {
                    list.addNodeSet(nodes);
                }
            } catch (ResourceModelSourceException e) {
                logger.error("Cannot get nodes from ["+nodesSource.toString()+"]: "+e.getMessage(), e);
                nodesSourceExceptions.add(new ResourceModelSourceException(
                    "Cannot get nodes from [" + nodesSource.toString() + "]: " + e.getMessage(), e));
            } catch (RuntimeException e) {
                logger.error("Cannot get nodes from ["+nodesSource.toString()+"]: "+e.getMessage(), e);
                nodesSourceExceptions.add(new ResourceModelSourceException(
                    "Cannot get nodes from [" + nodesSource.toString() + "]: " + e.getMessage(), e));
            }catch (Throwable e) {
                logger.error("Cannot get nodes from ["+nodesSource.toString()+"]: "+e.getMessage(), e);
                nodesSourceExceptions.add(new ResourceModelSourceException(
                    "Cannot get nodes from [" + nodesSource.toString() + "]: " + e.getMessage()));
            }
        }
        return list;

    }

    /**
     * Create a {@link NodeSetMerge} based on project configuration, it defaults to merge all node attributes unless "project.resources.mergeNodeAttributes" is false
     *
     * @return a NodeSetMerge
     */
    private NodeSetMerge getNodeSetMerge() {
        if (hasProperty(PROJECT_RESOURCES_MERGE_NODE_ATTRIBUTES) && "false".equals(getProperty
                (PROJECT_RESOURCES_MERGE_NODE_ATTRIBUTES))) {
            return new AdditiveListNodeSet();
        }
        return new MergedAttributesNodeSet();
    }

    /**
     * Conditionally update the nodes resources file if a URL source is defined for it and return
     * true if the update process was invoked and succeeded
     *
     * @return true if the update succeeded, false if it was not performed
     * @throws UpdateUtils.UpdateException if an error occurs while trying to update the resources file
     *
     */
    public boolean updateNodesResourceFile() throws UpdateUtils.UpdateException {
        if (shouldUpdateNodesResourceFile()) {
            updateNodesResourceFileFromUrl(getProperty(PROJECT_RESOURCES_URL_PROPERTY), null, null);
            return true;
        }
        return false;
    }

    /**
     * Update the nodes resources file from a specific URL, with BASIC authentication as provided or
     * as defined in the URL's userInfo section.
     * @param providerURL URL to retrieve resources file definition
     * @param username username or null
     * @param password or null
     * @throws com.dtolabs.rundeck.core.common.UpdateUtils.UpdateException if an error occurs during the update process
     */
    public void updateNodesResourceFileFromUrl(final String providerURL, final String username,
                                              final String password) throws UpdateUtils.UpdateException {
        if(!validateResourceProviderURL(providerURL)){
            throw new UpdateUtils.UpdateException("providerURL is not allowed: " + providerURL);
        }
        UpdateUtils.updateFileFromUrl(providerURL, getNodesResourceFilePath(), username, password,
            URLFileUpdater.factory());
        logger.debug("Updated nodes resources file: " + getNodesResourceFilePath());
    }

    /**
     * Return true if the URL is valid, and allowed by configuration
     */
    boolean validateResourceProviderURL(final String providerURL) throws UpdateUtils.UpdateException {
        final URL url;
        try {
            url= new URL(providerURL);
        } catch (MalformedURLException e) {
            throw new UpdateUtils.UpdateException("Invalid URL: " + providerURL, e);
        }
        //assert allowed URL scheme
        if(!("file".equals(url.getProtocol()) || "http".equals(url.getProtocol()) || "https".equals(url.getProtocol()))) {
            throw new UpdateUtils.UpdateException("URL protocol not allowed: " + url.getProtocol());
        }

        return isAllowedProviderURL(providerURL);
    }

    /**
     * Return true in these cases:
     *  1. project.properties allows URL and framework.properties allows URL.
     *  2. project.properties allows URL and no regexes are set in framework.properties
     *  3. project.properties no regexes are set, and framework.properites allows URL.
     */
    boolean isAllowedProviderURL(final String providerURL) {
        checkReloadProperties();
        //whitelist the configured providerURL
        if (hasProperty(PROJECT_RESOURCES_URL_PROPERTY) && getProperty(PROJECT_RESOURCES_URL_PROPERTY).equals(
            providerURL)) {
            return true;
        }
        //check regex properties for project props
        int i = 0;
        boolean projpass = false;
        boolean setproj = false;
        while (hasProperty(PROJECT_RESOURCES_ALLOWED_URL_PREFIX + i)) {
            setproj = true;
            final String regex = getProperty(PROJECT_RESOURCES_ALLOWED_URL_PREFIX + i);
            final Pattern pat = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
            final Matcher matcher = pat.matcher(providerURL);
            if (matcher.matches()) {
                logger.debug(
                    "ProviderURL allowed by project property \"project.resources.allowedURL." + i + "\": " + regex);
                projpass = true;
                break;
            }
            i++;
        }
        if (!projpass && setproj) {
            //was checked but failed match
            return false;
        }
        assert projpass ^ !setproj;
        //check framework props
        i = 0;

        final Framework framework = getFrameworkProjectMgr().getFramework();
        final boolean setframework = framework.hasProperty(FRAMEWORK_RESOURCES_ALLOWED_URL_PREFIX + i);
        if (!setframework && projpass) {
            //unset in framework.props, allowed by project.props
            return true;
        }
        if(!setframework && !setproj){
            //unset in both
            return false;
        }
        while (framework.hasProperty(FRAMEWORK_RESOURCES_ALLOWED_URL_PREFIX + i)) {
            final String regex = framework.getProperty(FRAMEWORK_RESOURCES_ALLOWED_URL_PREFIX + i);
            final Pattern pat = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
            final Matcher matcher = pat.matcher(providerURL);
            if (matcher.matches()) {
                logger.debug(
                    "ProviderURL allowed by framework property \"framework.resources.allowedURL." + i + "\": " + regex);
                //allowed by framework.props, and unset or allowed by project.props,
                return true;
            }
            i++;
        }
        if (projpass) {
            logger.warn("providerURL was allowed by project.properties, but is not allowed by framework.properties: "
                        + providerURL);
        }
        return false;
    }

    /**
     * Update the resources file from a source file
     *
     * @param source the source file
     * @throws UpdateUtils.UpdateException if an error occurs while trying to update the resources file
     *
     */
    public void updateNodesResourceFile(final File source) throws UpdateUtils.UpdateException {
        UpdateUtils.updateFileFromFile(source, getNodesResourceFilePath());
        logger.debug("Updated nodes resources file: " + getNodesResourceFilePath());
    }

    /**
     * Update the resources file given an input Nodes set
     *
     * @throws UpdateUtils.UpdateException if an error occurs while trying to update the resources file or generate
     * nodes
     *
     */
    public void updateNodesResourceFile(final INodeSet nodeset) throws UpdateUtils.UpdateException {
        final String nodesResourceFilePath = getNodesResourceFilePath();
        final ResourceFormatGenerator generator;
        File destfile = new File(getNodesResourceFilePath());
        try {
            generator =
                getFrameworkProjectMgr().getFramework().getResourceFormatGeneratorService()
                    .getGeneratorForFileExtension(destfile);
        } catch (UnsupportedFormatException e) {
            throw new UpdateUtils.UpdateException(
                "Unable to determine file format for file: " + nodesResourceFilePath,e);
        }
        File resfile = null;
        try {
            resfile = File.createTempFile("resource-temp", destfile.getName());
            resfile.deleteOnExit();
        } catch (IOException e) {
            throw new UpdateUtils.UpdateException("Unable to create temp file: " + e.getMessage(), e);
        }
        //serialize nodes and replace the nodes resource file

        try {
            final FileOutputStream stream = new FileOutputStream(resfile);
            try {
                generator.generateDocument(nodeset, stream);
            } finally {
                stream.close();
            }
        } catch (IOException e) {
            throw new UpdateUtils.UpdateException("Unable to generate resources file: " + e.getMessage(), e);
        } catch (ResourceFormatGeneratorException e) {
            throw new UpdateUtils.UpdateException("Unable to generate resources file: " + e.getMessage(), e);
        }

        updateNodesResourceFile(resfile);
        if(!resfile.delete()) {
            getLogger().warn("failed to remove temp file: " + resfile);
        }
        getLogger().debug("generated resources file: " + resfile.getAbsolutePath());
    }

    /**
     * Return true if the resources file should be pulled from the server If he node is the server and workbench
     * integration is enabled then the file should not be updated.
     *
     * @return
     */
    private boolean shouldUpdateNodesResourceFile() {
        return hasProperty(PROJECT_RESOURCES_URL_PROPERTY);
    }


    /**
     * Return the property value by name
     *
     * @param name
     * @return
     */
    public synchronized String getProperty(final String name) {
        checkReloadProperties();
        return lookup.getProperty(name);
    }


    public synchronized boolean hasProperty(final String key) {
        checkReloadProperties();
        return lookup.hasProperty(key);
    }
    public Map getProperties() {
        return lookup.getPropertiesMap();
    }


    /**
     * Return a PropertyRetriever interface for project-scoped properties
     */
    public synchronized PropertyRetriever getPropertyRetriever() {
        checkReloadProperties();
        return lookup.safe();
    }


   /**
     * Creates the file structure for a project
     *
     * @param projectDir     The project base directory
     * @throws IOException
     */
    public static void createFileStructure(final File projectDir) throws IOException {
       /*
       * create an empty project file structure
       */
        if (! projectDir.exists() && ! projectDir.mkdirs()) {
            throw new IOException("failed creating project base dir: " + projectDir.getAbsolutePath());
        }
       /**
        * Create project etc directory for configuration data
        */
        final File etcDir = new File(projectDir, FrameworkProject.ETC_DIR_NAME);
        if (! etcDir.exists() && ! etcDir.mkdirs()) {
            throw new IOException("failed creating project etc dir: " + etcDir.getAbsolutePath());
        }

    }

    /**
     * Create project.properties file based on $RDECK_BASE/etc/project.properties
     * @param overwrite Overwrite existing properties file
     */
    protected void generateProjectPropertiesFile(final boolean overwrite) {
        generateProjectPropertiesFile(overwrite, null, false);
    }

    /**
     * Create project.properties file based on $RDECK_BASE/etc/project.properties
     *
     * @param addDefaultProps
     * @param overwrite Overwrite existing properties file
     */
    protected void generateProjectPropertiesFile(final boolean overwrite, final Properties properties, boolean
            addDefaultProps) {
        generateProjectPropertiesFile(overwrite, properties, false, null, addDefaultProps);
    }

    /**
     * Create project.properties file based on $RDECK_BASE/etc/project.properties
     *
     * @param overwrite Overwrite existing properties file
     * @param properties properties to use
     * @param merge if true, merge existing properties that are not replaced
     * @param removePrefixes set of property prefixes to remove from original
     * @param addDefaultProps
     */
    protected void generateProjectPropertiesFile(final boolean overwrite, final Properties properties,
            final boolean merge, final Set<String> removePrefixes, boolean addDefaultProps) {
        final File destfile = getPropertyFile();
        if (destfile.exists() && !overwrite) {
            return;
        }
        final Properties newProps = new Properties();
        newProps.setProperty("project.name", getName());

        //TODO: improve default configuration generation
        if(addDefaultProps){
            if (null == properties || !properties.containsKey("resources.source.1.type") ) {
                //add default file source
                newProps.setProperty("resources.source.1.type", "file");
                newProps.setProperty("resources.source.1.config.file", new File(getEtcDir(), "resources.xml").getAbsolutePath());
                newProps.setProperty("resources.source.1.config.includeServerNode", "true");
                newProps.setProperty("resources.source.1.config.generateFileAutomatically", "true");
            }
            if(null==properties || !properties.containsKey("service.NodeExecutor.default.provider")) {
                newProps.setProperty("service.NodeExecutor.default.provider", "jsch-ssh");
            }
            if(null==properties || !properties.containsKey("service.FileCopier.default.provider")) {
                newProps.setProperty("service.FileCopier.default.provider", "jsch-scp");
            }
            if (null == properties || !properties.containsKey("project.ssh-keypath")) {
                newProps.setProperty("project.ssh-keypath", new File(System.getProperty("user.home"),
                        ".ssh/id_rsa").getAbsolutePath());
            }
            if(null==properties || !properties.containsKey("project.ssh-authentication")) {
                newProps.setProperty("project.ssh-authentication", "privateKey");
            }
        }
        if(merge) {
            final Properties orig = new Properties();

            if(destfile.exists()){
                try {
                    final FileInputStream fileInputStream = new FileInputStream(destfile);
                    try {
                        orig.load(fileInputStream);
                    } finally {
                        fileInputStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //add all original properties that are not in the incoming  properties, and are not
            //matched by one of the remove prefixes
            entry: for (final Object o : orig.entrySet()) {
                Map.Entry entry=(Map.Entry) o;
                //determine if
                final String key = (String) entry.getKey();
                if (null != removePrefixes) {
                    for (final String replacePrefix : removePrefixes) {
                        if (key.startsWith(replacePrefix)) {
                            //skip this key
                            continue entry;
                        }
                    }
                }
                newProps.put(entry.getKey(), entry.getValue());
            }
        }
        //overwrite original with the input properties
        if (null != properties) {
            newProps.putAll(properties);
        }

        try {
            final FileOutputStream fileOutputStream = new FileOutputStream(destfile);
            try {
                newProps.store(fileOutputStream, "Project " + getName() + " configuration, generated");
            } finally {
                fileOutputStream.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        getLogger().debug("generated project.properties: " + destfile.getAbsolutePath());
    }
    /**
     * Update the project properties file by setting updating the given properties, and removing
     * any properties that have a prefix in the removePrefixes set
     * @param properties new properties to put in the file
     * @param removePrefixes prefixes of properties to remove from the file
     */
    public void mergeProjectProperties(final Properties properties, final Set<String> removePrefixes) {
        generateProjectPropertiesFile(true, properties, true, removePrefixes, false);
    }
    /**
     * Set the project properties file contents exactly
     * @param properties new properties to use in the file
     */
    public void setProjectProperties(final Properties properties) {
        generateProjectPropertiesFile(true, properties, false, null, false);
    }

    /**
     * Checks if project is installed by checking if it's basedir directory exists.
     * @param d Depot object to check.
     * @return true if project exists
     */
    public static boolean isInstalled(final FrameworkProject d) {
        return d.getBaseDir().exists();
    }

    /**
     * Return the set of exceptions produced by the last attempt to invoke all node providers
     */
    public ArrayList<Exception> getResourceModelSourceExceptions() {
        return nodesSourceExceptions;
    }
}
TOP

Related Classes of com.dtolabs.rundeck.core.common.FrameworkProject

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.