com.vmware.admiral.request.ContainerRedeploymentTaskService.java Source code

Java tutorial

Introduction

Here is the source code for com.vmware.admiral.request.ContainerRedeploymentTaskService.java

Source

/*
 * Copyright (c) 2017 VMware, Inc. All Rights Reserved.
 *
 * This product is licensed to you under the Apache License, Version 2.0 (the "License").
 * You may not use this product except in compliance with the License.
 *
 * This product may include a number of subcomponents with separate copyright notices
 * and license terms. Your use of these subcomponents is subject to the terms and
 * conditions of the subcomponent's license, as noted in the LICENSE file.
 */

package com.vmware.admiral.request;

import static com.vmware.xenon.common.ServiceDocumentDescription.PropertyIndexingOption.STORE_ONLY;
import static com.vmware.xenon.common.ServiceDocumentDescription.PropertyUsageOption.REQUIRED;
import static com.vmware.xenon.common.ServiceDocumentDescription.PropertyUsageOption.SINGLE_ASSIGNMENT;

import java.util.HashMap;
import java.util.List;
import java.util.function.BiConsumer;

import org.apache.commons.lang3.StringUtils;

import com.vmware.admiral.common.ManagementUriParts;
import com.vmware.admiral.log.EventLogService;
import com.vmware.admiral.log.EventLogService.EventLogState;
import com.vmware.admiral.log.EventLogService.EventLogState.EventLogType;
import com.vmware.admiral.request.ContainerRedeploymentTaskService.ContainerRedeploymentTaskState.SubStage;
import com.vmware.admiral.request.RequestBrokerService.RequestBrokerState;
import com.vmware.admiral.request.utils.RequestUtils;
import com.vmware.admiral.service.common.AbstractTaskStatefulService;
import com.vmware.admiral.service.common.ServiceTaskCallback;
import com.vmware.photon.controller.model.resources.ComputeDescriptionService.ComputeDescription.ComputeType;
import com.vmware.xenon.common.LocalizableValidationException;
import com.vmware.xenon.common.Operation;
import com.vmware.xenon.common.TaskState.TaskStage;
import com.vmware.xenon.common.Utils;

/**
 * Task implementing the redeploying container request resource work flow.
 */
public class ContainerRedeploymentTaskService extends
        AbstractTaskStatefulService<ContainerRedeploymentTaskService.ContainerRedeploymentTaskState, ContainerRedeploymentTaskService.ContainerRedeploymentTaskState.SubStage> {
    public static final String FACTORY_LINK = ManagementUriParts.REQUEST_CONTAINER_REDEPLOYMENT_TASKS;

    public static final String DISPLAY_NAME = "Container Redeployment";

    public static class ContainerRedeploymentTaskState
            extends com.vmware.admiral.service.common.TaskServiceDocument<ContainerRedeploymentTaskState.SubStage> {

        public static enum SubStage {
            CREATED, CLUSTER_OUT, CLUSTER_IN, COMPLETED, ERROR;
        }

        /** The container description link. */
        @PropertyOptions(usage = { SINGLE_ASSIGNMENT, REQUIRED }, indexing = STORE_ONLY)
        public String containerDescriptionLink;

        /** The container state links to be redeployed. */
        @PropertyOptions(usage = { SINGLE_ASSIGNMENT, REQUIRED }, indexing = STORE_ONLY)
        public List<String> containerStateLinks;

        /** The context_id in which the container to be redeployed */
        @PropertyOptions(usage = { SINGLE_ASSIGNMENT, REQUIRED }, indexing = STORE_ONLY)
        public String contextId;

        /** The desired containers in cluster */
        @PropertyOptions(usage = { SINGLE_ASSIGNMENT, REQUIRED }, indexing = STORE_ONLY)
        public int desiredClusterSize;
    }

    public ContainerRedeploymentTaskService() {
        super(ContainerRedeploymentTaskState.class, SubStage.class, DISPLAY_NAME);
        super.toggleOption(ServiceOption.PERSISTENCE, true);
        super.toggleOption(ServiceOption.REPLICATION, true);
        super.toggleOption(ServiceOption.OWNER_SELECTION, true);
        super.toggleOption(ServiceOption.INSTRUMENTATION, true);
    }

    @Override
    protected void handleStartedStagePatch(ContainerRedeploymentTaskState state) {
        switch (state.taskSubStage) {
        case CREATED:
            logFine("Start redeploying containers.");
            proceedTo(SubStage.CLUSTER_OUT);
            break;
        case CLUSTER_OUT:
            sendClusterRequest(state, SubStage.CLUSTER_OUT);
            break;
        case CLUSTER_IN:
            sendClusterRequest(state, SubStage.CLUSTER_IN);
            break;
        case COMPLETED:
            completeTask(state);
            break;
        case ERROR:
            completeTaskWithError(state);
            break;
        default:
            break;
        }
    }

    @Override
    protected void validateStateOnStart(ContainerRedeploymentTaskState state) {
        if (state.containerDescriptionLink == null || state.containerDescriptionLink.isEmpty()) {
            throw new LocalizableValidationException("'containerDescriptionLink' must not be empty",
                    "request.container.redeployment.containerDescriptionLink.empty");
        }

        if (state.containerStateLinks == null || state.containerStateLinks.isEmpty()) {
            throw new LocalizableValidationException("'containerStateLinks' must not be empty",
                    "request.container.redeployment.containerStateLinks.empty");
        }

        if (state.contextId == null || state.contextId.isEmpty()) {
            throw new LocalizableValidationException("'contextId' must not be empty",
                    "request.container.redeployment.contextId.empty");
        }
    }

    private void completeTask(ContainerRedeploymentTaskState state) {
        String redeployedContainers = StringUtils.join(state.containerStateLinks, ", ");
        // TODO: get only the id, not whole self link
        String description = String.format("Containers [%s] redeployed", redeployedContainers);

        sendEventLog(state.tenantLinks, description, (op, ex) -> {
            if (ex != null) {
                op.fail(new LocalizableValidationException("Failed to publish an event log",
                        "request.container.redeployment.event-log.create-fail"));
            }

            complete();
        });
    }

    private void completeTaskWithError(ContainerRedeploymentTaskState state) {
        String redeployedContainers = StringUtils.join(state.containerStateLinks, ", ");
        // TODO: get only the id, not whole self link
        String description = String.format("Containers [%s] failed to redeploy", redeployedContainers);

        sendEventLog(state.tenantLinks, description, (op, ex) -> {
            if (ex != null) {
                op.fail(new LocalizableValidationException("Failed to publish an event log",
                        "request.container.redeployment.event-log.create-fail"));
            }

            completeWithError();
        });
    }

    private void sendClusterRequest(ContainerRedeploymentTaskState state, SubStage currentStage) {

        String cdLink = state.containerDescriptionLink;
        String contextId = state.contextId;
        int clusterSize = state.desiredClusterSize;

        // TODO: extract this logic in a separate stage
        if (SubStage.CLUSTER_OUT.equals(currentStage)) {
            clusterSize = state.desiredClusterSize + state.containerStateLinks.size();
        }

        logFine("Cluster container with %s description link, from %s context_id with cluster size: %s", cdLink,
                contextId, clusterSize);

        RequestBrokerState rbState = new RequestBrokerState();
        rbState.resourceDescriptionLink = cdLink;
        rbState.resourceCount = clusterSize;
        rbState.customProperties = new HashMap<>();
        rbState.customProperties.put(RequestUtils.FIELD_NAME_CONTEXT_ID_KEY, contextId);
        rbState.resourceType = ComputeType.DOCKER_CONTAINER.name().toString();
        rbState.operation = RequestBrokerState.CLUSTER_RESOURCE_OPERATION;
        rbState.tenantLinks = state.tenantLinks;
        if (SubStage.CLUSTER_OUT.equals(currentStage)) {
            rbState.serviceTaskCallback = ServiceTaskCallback.create(getSelfLink(), TaskStage.STARTED,
                    SubStage.CLUSTER_IN, TaskStage.STARTED, SubStage.ERROR);
        } else {
            rbState.serviceTaskCallback = ServiceTaskCallback.create(getSelfLink(), TaskStage.STARTED,
                    SubStage.COMPLETED, TaskStage.STARTED, SubStage.ERROR);
        }

        sendRequest(Operation.createPost(this, RequestBrokerFactoryService.SELF_LINK).setBody(rbState)
                .setCompletion((o, e) -> {
                    if (e != null) {
                        logSevere(Utils.toString(e));
                        return;
                    }
                }));
    }

    private void sendEventLog(List<String> tenantLinks, String description,
            BiConsumer<Operation, Throwable> callback) {
        EventLogState eventLog = new EventLogState();
        eventLog.tenantLinks = tenantLinks;
        eventLog.resourceType = getClass().getName();
        eventLog.eventLogType = EventLogType.INFO;
        eventLog.description = description;

        sendRequest(Operation.createPost(getHost(), EventLogService.FACTORY_LINK).setBody(eventLog)
                .setCompletion((op, ex) -> {
                    if (ex != null) {
                        callback.accept(op, ex);
                    }

                    callback.accept(op, null);
                }));
    }
}