com.pinterest.deployservice.handler.EnvironHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.pinterest.deployservice.handler.EnvironHandler.java

Source

/*
 * Copyright 2016 Pinterest, Inc.
 *
 * 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.pinterest.deployservice.handler;

import com.pinterest.deployservice.ServiceContext;
import com.pinterest.deployservice.bean.*;
import com.pinterest.deployservice.common.*;
import com.pinterest.deployservice.dao.AgentDAO;
import com.pinterest.deployservice.dao.EnvironDAO;
import com.pinterest.deployservice.dao.GroupDAO;
import com.pinterest.deployservice.dao.HostDAO;
import com.pinterest.deployservice.dao.PromoteDAO;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

public class EnvironHandler {
    private static final Logger LOG = LoggerFactory.getLogger(EnvironHandler.class);

    private EnvironDAO environDAO;
    private PromoteDAO promoteDAO;
    private AgentDAO agentDAO;
    private GroupDAO groupDAO;
    private HostDAO hostDAO;
    private CommonHandler commonHandler;
    private DataHandler dataHandler;

    public EnvironHandler(ServiceContext serviceContext) {
        environDAO = serviceContext.getEnvironDAO();
        promoteDAO = serviceContext.getPromoteDAO();
        agentDAO = serviceContext.getAgentDAO();
        groupDAO = serviceContext.getGroupDAO();
        hostDAO = serviceContext.getHostDAO();
        commonHandler = new CommonHandler(serviceContext);
        dataHandler = new DataHandler(serviceContext);
    }

    void normalizeEnvRequest(EnvironBean envBean, String operator) throws Exception {
        if (envBean.getSuccess_th() != null) {
            Integer successTh = envBean.getSuccess_th();
            if (successTh > 10000 || successTh < 0) {
                throw new DeployInternalException("Success threshold should between 0 and 10000!");
            }
            envBean.setSuccess_th(successTh);
        }

        if (envBean.getMax_deploy_num() != null) {
            int max = envBean.getMax_deploy_num();
            if (max > 0) {
                envBean.setMax_deploy_num(max);
            } else {
                throw new DeployInternalException("Max deploys to keep is below 0!");
            }
        }

        if (envBean.getMax_deploy_day() != null) {
            int max = envBean.getMax_deploy_day();
            if (max > 0) {
                envBean.setMax_deploy_day(max);
            } else {
                throw new DeployInternalException("Max days to keep a deploy is below 0!");
            }
        }

        envBean.setLast_operator(operator);
        envBean.setLast_update(System.currentTimeMillis());
    }

    void updateEnvBeanDefault(EnvironBean envBean) throws Exception {
        if (envBean.getEnv_state() == null) {
            envBean.setEnv_state(EnvState.NORMAL);
        }

        if (envBean.getDescription() == null) {
            envBean.setDescription(
                    String.format("%s stage for env %s", envBean.getEnv_name(), envBean.getStage_name()));
        }

        if (envBean.getBuild_name() == null) {
            envBean.setBuild_name(envBean.getEnv_name());
        }

        if (envBean.getBranch() == null) {
            envBean.setBranch(Constants.DEFAULT_BRANCH_NAME);
        }

        if (envBean.getMax_parallel() == null) {
            envBean.setMax_parallel(Constants.DEFAULT_MAX_PARALLEL_HOSTS);
        }

        if (envBean.getPriority() == null) {
            envBean.setPriority(Constants.DEFAULT_PRIORITY);
        }

        if (envBean.getStuck_th() == null) {
            envBean.setStuck_th(Constants.DEFAULT_STUCK_THRESHOLD);
        }

        if (envBean.getSuccess_th() == null) {
            //To keep the precision, the default success_th value should be 10000 in DB.
            envBean.setSuccess_th(Constants.DEFAULT_SUCCESS_THRESHOLD * 100);
        }

        if (envBean.getAccept_type() == null) {
            envBean.setAccept_type(Constants.DEFAULT_ACCEPTANCE_TYPE);
        }

        if (envBean.getMax_deploy_num() == null) {
            envBean.setMax_deploy_num(Constants.DEFAULT_DEPLOY_NUM);
        }

        if (envBean.getMax_deploy_day() == null) {
            envBean.setMax_deploy_day(Constants.DEFAULT_DEPLOY_DAY);
        }
    }

    void updatePromoteBeanDefault(PromoteBean promoteBean) throws Exception {
        if (promoteBean.getType() == null) {
            promoteBean.setType(Constants.DEFAULT_PROMOTE_TYPE);
        }

        if (promoteBean.getQueue_size() == null) {
            promoteBean.setQueue_size(Constants.DEFAULT_MAX_PROMOTE_QUEUE_SIZE);
        }

        if (promoteBean.getDelay() == null) {
            promoteBean.setDelay(Constants.DEFAULT_PROMOTE_DELAY_MINUTES);
        }

        if (promoteBean.getDisable_policy() == null) {
            promoteBean.setDisable_policy(Constants.DEFAULT_PROMOTE_DISABLE_POLICY);
        }

        if (promoteBean.getFail_policy() == null) {
            promoteBean.setFail_policy(Constants.DEFAULT_PROMOTE_FAIL_POLICY);
        }
    }

    public String createEnvStage(EnvironBean envBean, String operator) throws Exception {
        normalizeEnvRequest(envBean, operator);
        updateEnvBeanDefault(envBean);
        String envId = CommonUtils.getBase64UUID();
        envBean.setEnv_id(envId);
        environDAO.insert(envBean);
        return envId;
    }

    public List<AlarmBean> getAlarms(EnvironBean environBean) throws Exception {
        String id = environBean.getAlarm_config_id();
        if (StringUtils.isEmpty(id)) {
            return Collections.emptyList();
        }
        return dataHandler.getDataById(id, AlarmDataFactory.class);
    }

    public void updateAlarms(EnvironBean environBean, List<AlarmBean> alarmBeans, String operator)
            throws Exception {
        String id = environBean.getAlarm_config_id();
        if (StringUtils.isEmpty(id)) {
            id = dataHandler.insertData(alarmBeans, AlarmDataFactory.class, operator);
            EnvironBean updateBean = new EnvironBean();
            updateBean.setAlarm_config_id(id);
            updateStage(environBean, updateBean, operator);
        } else {
            dataHandler.updateData(id, alarmBeans, AlarmDataFactory.class, operator);
        }
    }

    public List<MetricsConfigBean> getMetrics(EnvironBean environBean) throws Exception {
        String id = environBean.getMetrics_config_id();
        if (StringUtils.isEmpty(id)) {
            return Collections.emptyList();
        }
        return dataHandler.getDataById(id, MetricsDataFactory.class);
    }

    public void updateMetrics(EnvironBean environBean, List<MetricsConfigBean> metricsBeans, String operator)
            throws Exception {
        String id = environBean.getMetrics_config_id();
        if (StringUtils.isEmpty(id)) {
            id = dataHandler.insertData(metricsBeans, MetricsDataFactory.class, operator);
            EnvironBean updateBean = new EnvironBean();
            updateBean.setMetrics_config_id(id);
            updateStage(environBean, updateBean, operator);
        } else {
            dataHandler.updateData(id, metricsBeans, MetricsDataFactory.class, operator);
        }
    }

    public EnvWebHookBean getHooks(EnvironBean environBean) throws Exception {
        String id = environBean.getWebhooks_config_id();
        if (StringUtils.isEmpty(id)) {
            return new EnvWebHookBean();
        }
        return dataHandler.getDataById(id, WebhookDataFactory.class);
    }

    public void updateHooks(EnvironBean environBean, EnvWebHookBean hookBean, String operator) throws Exception {
        String id = environBean.getWebhooks_config_id();
        if (StringUtils.isEmpty(id)) {
            id = dataHandler.insertData(hookBean, WebhookDataFactory.class, operator);
            EnvironBean updateBean = new EnvironBean();
            updateBean.setWebhooks_config_id(id);
            updateStage(environBean, updateBean, operator);
        } else {
            dataHandler.updateData(id, hookBean, WebhookDataFactory.class, operator);
        }
    }

    public Map<String, String> getAdvancedConfigs(EnvironBean environBean) throws Exception {
        String id = environBean.getAdv_config_id();
        if (StringUtils.isEmpty(id)) {
            return Collections.emptyMap();
        }
        return dataHandler.getMapById(id);
    }

    public void updateAdvancedConfigs(EnvironBean environBean, Map<String, String> configs, String operator)
            throws Exception {
        String dataId = environBean.getAdv_config_id();
        if (dataId == null) {
            // Create data the first time
            dataId = dataHandler.insertMap(configs, operator);
            EnvironBean updateBean = new EnvironBean();
            updateBean.setAdv_config_id(dataId);
            updateStage(environBean, updateBean, operator);
        } else {
            dataHandler.updateMap(dataId, configs, operator);
        }
    }

    public Map<String, String> getScriptConfigs(EnvironBean environBean) throws Exception {
        String id = environBean.getSc_config_id();
        if (StringUtils.isEmpty(id)) {
            return Collections.emptyMap();
        }
        return dataHandler.getMapById(id);
    }

    public void updateScriptConfigs(EnvironBean environBean, Map<String, String> configs, String operator)
            throws Exception {
        String dataId = environBean.getSc_config_id();
        if (dataId == null) {
            // Create data the first time
            dataId = dataHandler.insertMap(configs, operator);
            EnvironBean updateBean = new EnvironBean();
            updateBean.setSc_config_id(dataId);
            updateStage(environBean, updateBean, operator);
        } else {
            dataHandler.updateMap(dataId, configs, operator);
        }
    }

    public void updateStage(EnvironBean origBean, EnvironBean updateBean, String operator) throws Exception {
        normalizeEnvRequest(updateBean, operator);
        environDAO.update(origBean.getEnv_name(), origBean.getStage_name(), updateBean);
    }

    PromoteBean genDefaultEnvPromote(String envId) {
        PromoteBean promote = new PromoteBean();
        promote.setEnv_id(envId);
        promote.setType(Constants.DEFAULT_PROMOTE_TYPE);
        promote.setQueue_size(Constants.DEFAULT_PROMOTE_QUEUE_SIZE);
        promote.setDelay(Constants.DEFAULT_PROMOTE_DELAY_MINUTES);
        promote.setDisable_policy(Constants.DEFAULT_PROMOTE_DISABLE_POLICY);
        promote.setFail_policy(Constants.DEFAULT_PROMOTE_FAIL_POLICY);
        return promote;
    }

    public PromoteBean getEnvPromote(String envName, String stageName) throws Exception {
        EnvironBean envBean = environDAO.getByStage(envName, stageName);
        PromoteBean promoteBean = promoteDAO.getById(envBean.getEnv_id());
        if (promoteBean == null) {
            return genDefaultEnvPromote(envBean.getEnv_id());
        }
        return promoteBean;
    }

    public void updateEnvPromote(EnvironBean envBean, PromoteBean promoteBean, String operator) throws Exception {
        String envId = envBean.getEnv_id();
        promoteBean.setLast_operator(operator);
        promoteBean.setLast_update(System.currentTimeMillis());

        PromoteBean originBean = promoteDAO.getById(envId);
        if (originBean == null) {
            // Provide all the defaults if this is an insert
            updatePromoteBeanDefault(promoteBean);
            promoteBean.setEnv_id(envId);
            promoteDAO.insert(promoteBean);
        } else {
            promoteDAO.update(envId, promoteBean);
        }
    }

    public String resume(EnvironBean envBean, String operator) throws Exception {
        EnvironBean updateBean = new EnvironBean();
        updateBean.setEnv_state(EnvState.NORMAL);
        updateStage(envBean, updateBean, operator);
        return envBean.getEnv_id();
    }

    public String pause(EnvironBean envBean, String operator) throws Exception {
        EnvironBean updateBean = new EnvironBean();
        updateBean.setEnv_state(EnvState.PAUSED);
        updateStage(envBean, updateBean, operator);
        return envBean.getEnv_id();
    }

    /**
     * A stage is only allowed to be deleted when there is no host and group capacity, e.g.
     * all the agents had been instructed to delete its env ( stop service and delete status etc.)
     */
    public void deleteEnvStage(String envName, String envStage, String operator) throws Exception {
        EnvironBean envBean = getStageSafely(envName, envStage);
        String envId = envBean.getEnv_id();
        List<String> groups = groupDAO.getCapacityGroups(envBean.getEnv_id());
        if (groups != null && !groups.isEmpty()) {
            throw new DeployInternalException("Reject the delete of env %s while it still has group capacity",
                    envId);
        }
        List<String> hosts = groupDAO.getCapacityHosts(envBean.getEnv_id());
        if (hosts != null && !hosts.isEmpty()) {
            throw new DeployInternalException("Reject the delete of env %s while it still has host capacity",
                    envId);
        }
        /* TODO should we do the following check?
        long total = agentDAO.countAgentByEnv(envId);
        if (total > 0) {
        throw new DeployInternalException("Reject the delete of env %s while there are still %d hosts active", envId, total);
        }
        */

        // TODO make the following transcational
        environDAO.delete(envId);
        promoteDAO.delete(envId);
        if (envBean.getAdv_config_id() != null) {
            dataHandler.deleteData(envBean.getAdv_config_id());
        }
        if (envBean.getSc_config_id() != null) {
            dataHandler.deleteData(envBean.getSc_config_id());
        }
        if (envBean.getAlarm_config_id() != null) {
            dataHandler.deleteData(envBean.getAlarm_config_id());
        }
        if (envBean.getMetrics_config_id() != null) {
            dataHandler.deleteData(envBean.getMetrics_config_id());
        }
    }

    /**
     * UI should check the host exist and can be added first;
     * Make sure UI warn if cause env conflict with existing group capacity
     */
    public void updateHosts(EnvironBean envBean, List<String> hosts, String operator) throws Exception {
        List<String> oldHostList = groupDAO.getCapacityHosts(envBean.getEnv_id());
        Set<String> oldHosts = new HashSet<>();
        oldHosts.addAll(oldHostList);
        for (String host : hosts) {
            if (!oldHosts.contains(host)) {
                groupDAO.addHostCapacity(envBean.getEnv_id(), host);
            } else {
                oldHosts.remove(host);
            }
        }
        for (String host : oldHosts) {
            groupDAO.removeHostCapacity(envBean.getEnv_id(), host);
        }
    }

    public void updateGroups(EnvironBean envBean, List<String> groups, String operator) throws Exception {
        // TODO need to check group env conflicts and reject if so
        List<String> oldGroupList = groupDAO.getCapacityGroups(envBean.getEnv_id());
        Set<String> oldGroups = new HashSet<>();
        oldGroups.addAll(oldGroupList);
        for (String group : groups) {
            if (!oldGroups.contains(group)) {
                groupDAO.addGroupCapacity(envBean.getEnv_id(), group);
            } else {
                oldGroups.remove(group);
            }
        }
        for (String group : oldGroups) {
            groupDAO.removeGroupCapacity(envBean.getEnv_id(), group);
        }
    }

    /**
     * Take this opportunity to update the deploy progress, and return the latest
     * This usually called by UI client to monitor the ongoing deploy.
     */
    public DeployProgressBean updateDeployProgress(EnvironBean envBean) throws Exception {
        // TODO consider to transition and get agent status in one transaction for consistency
        commonHandler.transitionDeployState(envBean.getDeploy_id(), envBean);
        List<AgentBean> agentBeans = agentDAO.getAllByEnv(envBean.getEnv_id());

        long capacityTotal = environDAO.countTotalCapacity(envBean.getEnv_id(), envBean.getEnv_name(),
                envBean.getStage_name());
        Set<String> capacityHosts = new HashSet<>();
        if (capacityTotal > agentBeans.size()) {
            // we only consider the missing hosts if there is a hint
            List<String> capacityHostList = environDAO.getTotalCapacityHosts(envBean.getEnv_id(),
                    envBean.getEnv_name(), envBean.getEnv_state().toString());
            capacityHosts.addAll(capacityHostList);
        }

        DeployProgressBean progress = new DeployProgressBean();
        List<AgentBean> agents = new ArrayList<>(agentBeans.size());
        for (AgentBean agentBean : agentBeans) {
            agents.add(agentBean);
            // yep, we've seen it
            if (!capacityHosts.isEmpty()) {
                capacityHosts.remove(agentBean.getHost_name());
            }
        }

        List<HostBean> newHosts = new ArrayList<>();
        for (Iterator<String> iterator = capacityHosts.iterator(); iterator.hasNext();) {
            HostBean hostBean = hostDAO.getByEnvIdAndHostName(envBean.getEnv_id(), iterator.next());
            if (hostBean != null) {
                iterator.remove();
                newHosts.add(hostBean);
            }
        }

        progress.setMissingHosts(new ArrayList<>(capacityHosts));
        progress.setAgents(agents);
        progress.setProvisioningHosts(newHosts);
        return progress;
    }

    EnvironBean getStageSafely(String envName, String envStage) throws Exception {
        EnvironBean envBean = environDAO.getByStage(envName, envStage);
        if (envBean == null) {
            throw new DeployInternalException("env %s/%s does not exist.", envName, envStage);
        }
        return envBean;
    }

    public List<String> getMissingHosts(EnvironBean envBean) throws Exception {
        List<AgentBean> agentBeans = agentDAO.getAllByEnv(envBean.getEnv_id());
        long capacityTotal = environDAO.countTotalCapacity(envBean.getEnv_id(), envBean.getEnv_name(),
                envBean.getStage_name());
        Set<String> capacityHosts = new HashSet<>();
        if (capacityTotal > agentBeans.size()) {
            // we only consider the missing hosts if there is a hint
            List<String> capacityHostList = environDAO.getTotalCapacityHosts(envBean.getEnv_id(),
                    envBean.getEnv_name(), envBean.getEnv_state().toString());
            capacityHosts.addAll(capacityHostList);
        }

        // Remove hosts agent has seen
        for (AgentBean agentBean : agentBeans) {
            if (!capacityHosts.isEmpty()) {
                capacityHosts.remove(agentBean.getHost_name());
            }
        }

        for (Iterator<String> iterator = capacityHosts.iterator(); iterator.hasNext();) {
            HostBean hostBean = hostDAO.getByEnvIdAndHostName(envBean.getEnv_id(), iterator.next());
            if (hostBean != null) {
                iterator.remove();
            }
        }
        return new ArrayList<>(capacityHosts);
    }
}