| /* |
| * 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.brooklyn.rest.client; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| |
| import java.util.Date; |
| import java.util.concurrent.Callable; |
| import java.util.concurrent.atomic.AtomicReference; |
| import javax.ws.rs.core.Response; |
| |
| import org.apache.brooklyn.rest.api.EffectorApi; |
| import org.apache.brooklyn.rest.domain.Status; |
| import org.apache.brooklyn.rest.domain.TaskSummary; |
| import org.apache.brooklyn.util.repeat.Repeater; |
| import org.apache.brooklyn.util.time.Duration; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import com.google.common.collect.ImmutableMap; |
| |
| public class BrooklynApiUtil { |
| |
| private static final Logger LOG = LoggerFactory.getLogger(BrooklynApiUtil.class); |
| private static final Duration DEFAULT_POLL_PERIOD = Duration.FIVE_SECONDS; |
| private static final Duration DEFAULT_TIMEOUT = Duration.FIVE_MINUTES; |
| |
| private BrooklynApiUtil() {} |
| |
| /** |
| * Deploys the blueprint and returns the task summary. |
| * @throws Exception If the response from the server when deploying was {@link #isUnhealthyResponse unhealthy}. |
| */ |
| public static TaskSummary deployBlueprint(BrooklynApi api, String blueprint) throws Exception { |
| Response r = api.getApplicationApi().createFromYaml(blueprint); |
| if (isUnhealthyResponse(r)) { |
| throw new Exception("Unexpected response deploying blueprint to server: " + r.getStatus()); |
| } else { |
| LOG.debug("Server response to deploy blueprint: " + r.getStatus()); |
| } |
| return BrooklynApi.getEntity(r, TaskSummary.class); |
| } |
| |
| /** |
| * Waits for the application with the given ID to be running. |
| * |
| * @throws IllegalStateException If the application was not running after {@link #DEFAULT_TIMEOUT}. |
| */ |
| public static void waitForRunningAndThrowOtherwise(BrooklynApi api, String applicationId, String taskId) throws IllegalStateException { |
| waitForRunningAndThrowOtherwise(api, applicationId, taskId, DEFAULT_TIMEOUT); |
| } |
| |
| /** |
| * Waits for the application with the given ID to be running. |
| * |
| * @throws IllegalStateException If the application was not running after the given timeout. |
| */ |
| public static void waitForRunningAndThrowOtherwise(BrooklynApi api, String applicationId, String taskId, Duration timeout) throws IllegalStateException { |
| Status finalStatus = waitForAppStatus(api, applicationId, Status.RUNNING, timeout, DEFAULT_POLL_PERIOD); |
| if (!Status.RUNNING.equals(finalStatus)) { |
| LOG.error("Application is not running. Is: " + finalStatus.name().toLowerCase()); |
| |
| StringBuilder message = new StringBuilder(); |
| message.append("Application ").append(applicationId) |
| .append(" should be running but is ").append(finalStatus.name().toLowerCase()) |
| .append(". "); |
| |
| if (Status.ERROR.equals(finalStatus) || Status.UNKNOWN.equals(finalStatus)) { |
| String result = getTaskResult(api, taskId); |
| message.append("\nThe result of the task on the server was:\n") |
| .append(result); |
| } |
| throw new IllegalStateException(message.toString()); |
| } |
| } |
| |
| /** |
| * Polls Brooklyn until the given application has the given status. Quits early if the |
| * application's status is {@link org.apache.brooklyn.rest.domain.Status#ERROR} or |
| * {@link org.apache.brooklyn.rest.domain.Status#UNKNOWN} and desiredStatus is something else. |
| * |
| * @return The final polled status. |
| */ |
| public static Status waitForAppStatus(final BrooklynApi api, final String application, final Status desiredStatus, |
| Duration timeout, Duration pollPeriod) { |
| final AtomicReference<Status> appStatus = new AtomicReference<>(Status.UNKNOWN); |
| final boolean shortcutOnError = !Status.ERROR.equals(desiredStatus) && !Status.UNKNOWN.equals(desiredStatus); |
| LOG.info("Waiting " + timeout + " from " + new Date() + " for application " + application + " to be " + desiredStatus); |
| Repeater.create("Waiting for application " + application + " status to be " + desiredStatus) |
| .every(pollPeriod) |
| .limitTimeTo(timeout) |
| .rethrowExceptionImmediately() |
| .until(new Callable<Boolean>() { |
| @Override |
| public Boolean call() throws Exception { |
| Status status = api.getApplicationApi().get(application).getStatus(); |
| LOG.debug("Application " + application + " status is: " + status); |
| appStatus.set(status); |
| return desiredStatus.equals(status) || (shortcutOnError && |
| (Status.ERROR.equals(status) || Status.UNKNOWN.equals(status))); |
| } |
| }) |
| .run(); |
| if (appStatus.get().equals(desiredStatus)) { |
| LOG.info("Application " + application + " is " + desiredStatus.name()); |
| } else { |
| LOG.warn("Application is not " + desiredStatus.name() + " within " + timeout + |
| ". Status is: " + appStatus.get()); |
| } |
| return appStatus.get(); |
| } |
| |
| /** |
| * Use the {@link EffectorApi effector API} to invoke the stop effector on the given application. |
| */ |
| public static void attemptStop(BrooklynApi api, String application, Duration timeout) { |
| api.getEffectorApi().invoke(application, application, "stop", String.valueOf(timeout.toMilliseconds()), |
| ImmutableMap.<String, Object>of()); |
| } |
| |
| /** |
| * @return The result of the task with the given id, or "unknown" if it could not be found. |
| */ |
| public static String getTaskResult(BrooklynApi api, String taskId) { |
| checkNotNull(taskId, "taskId"); |
| TaskSummary summary = api.getActivityApi().get(taskId); |
| return summary == null || summary.getResult() == null ? "unknown" : summary.getResult().toString(); |
| } |
| |
| /** |
| * @return true if response's status code is not between 200 and 299 inclusive. |
| */ |
| public static boolean isUnhealthyResponse(Response response) { |
| return response.getStatus() < 200 || response.getStatus() >= 300; |
| } |
| |
| } |