All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.danielflower.apprunner.router.web.CreateAppHandler Maven / Gradle / Ivy

There is a newer version: 1.11.6
Show newest version
package com.danielflower.apprunner.router.web;

import com.danielflower.apprunner.router.App;
import com.danielflower.apprunner.router.mgmt.Cluster;
import com.danielflower.apprunner.router.mgmt.Runner;
import io.muserver.HeaderNames;
import io.muserver.MuRequest;
import io.muserver.MuResponse;
import io.muserver.RouteHandler;
import io.muserver.murp.ReverseProxy;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpField;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.ServerErrorException;
import java.net.URI;
import java.util.*;

public class CreateAppHandler implements RouteHandler {
    private static final Logger log = LoggerFactory.getLogger(CreateAppHandler.class);
    private final ProxyMap proxyMap;
    private final Cluster cluster;
    private final HttpClient client;


    public CreateAppHandler(ProxyMap proxyMap, Cluster cluster, HttpClient client) {
        this.proxyMap = proxyMap;
        this.cluster = cluster;
        this.client = client;
    }

    @Override
    public void handle(MuRequest request, MuResponse response, Map pathParams) {
        try {
            log.info("Going to create an app");

            List excludedRunnerIDs = new ArrayList<>(request.headers().getAll("X-Excluded-Runner"));
            String createBody = request.readBodyAsString();

            boolean finished = false;
            while (!finished) {
                Optional targetRunner = cluster.allocateRunner(proxyMap.getAll(), excludedRunnerIDs);
                if (targetRunner.isPresent()) {
                    URI targetAppRunner = targetRunner.get().url.resolve("/api/v1/apps");
                    org.eclipse.jetty.client.api.Request creationReq = client.POST(targetAppRunner)
                        .content(new StringContentProvider(createBody));

                    ReverseProxy.setRequestHeaders(request, creationReq, false, true, "HTTP/1.1 " + App.VIA_VALUE);
                    creationReq.header("accept", "*/*"); // for old apprunner instances

                    ContentResponse creationResp;
                    try {
                        creationResp = creationReq.send();
                        if ((creationResp.getStatus() / 100) == 5) {
                            String contentAsString = creationResp.getContentAsString();
                            throw new RuntimeException("Got a " + creationResp.getStatus() + " response - " + contentAsString);
                        }
                        log.info("Proxying app creation with " + creationResp);

                    } catch (InterruptedException e) {
                        break;
                    } catch (Exception e) {
                        targetRunner.get().refreshRunnerCountCache(proxyMap.getAll());
                        excludedRunnerIDs.add(targetRunner.get().id);
                        log.warn("Error while calling POST " + targetAppRunner + " to create a new app. Will retry if" +
                            " there are more runners. Error was " + e.getClass().getName() + " " + e.getMessage());
                        continue;
                    }
                    response.status(creationResp.getStatus());
                    response.headers().remove(HeaderNames.DATE);

                    Set hopHeaders = ReverseProxy.HOP_BY_HOP_HEADERS;
                    for (HttpField header : creationResp.getHeaders()) {
                        String hn = header.getName().toLowerCase();
                        if (!hopHeaders.contains(hn) && !hn.equals("content-encoding")) {
                            response.headers().add(header.getName(), header.getValue());
                        }
                    }
                    String content = creationResp.getContentAsString();
                    if (creationResp.getStatus() == 201) {
                        log.info("Created new app: " + content);
                        JSONObject resp = new JSONObject(content);
                        String appName = resp.getString("name");
                        proxyMap.add(appName, targetAppRunner.resolve("/" + appName));
                    }
                    finished = true;
                    response.write(content);

                } else {
                    finished = true;
                    log.error("There are no app runner instances available! Add another instance or change the maxApps value of an existing one.");
                    response.status(503);
                    response.write("There are no App Runner instances with free capacity");
                }
            }
        } catch (Exception e) {
            String errorID = "ERR" + UUID.randomUUID();
            log.error("Error creating an app. Error ID=" + errorID, e);
            throw new ServerErrorException("Error while creating app. Error ID=" + errorID, 502);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy