package io.cattle.platform.docker.machine.launch; import static io.cattle.platform.server.context.ServerContext.*; import io.cattle.platform.archaius.util.ArchaiusUtil; import io.cattle.platform.core.model.Credential; import io.cattle.platform.hazelcast.membership.ClusterService; import io.cattle.platform.hazelcast.membership.ClusteredMember; import io.cattle.platform.lock.definition.LockDefinition; import io.cattle.platform.service.launcher.GenericServiceLauncher; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.inject.Inject; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.client.fluent.Request; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.netflix.config.DynamicStringProperty; public class WebsocketProxyLauncher extends GenericServiceLauncher { private static final Logger log = LoggerFactory.getLogger(WebsocketProxyLauncher.class); private static final String MASTER_CONF = "master.conf"; private static final DynamicStringProperty ACCESS_LOG = ArchaiusUtil.getString("access.log"); private static final DynamicStringProperty API_INTERCEPTOR_CONFIG = ArchaiusUtil.getString("api.interceptor.config"); private static final DynamicStringProperty API_INTERCEPTOR_CONFIG_FILE = ArchaiusUtil.getString("api.interceptor.config.file"); @Inject ClusterService clusterService; String written = "start"; @Override protected List<DynamicStringProperty> getReloadSettings() { List<DynamicStringProperty> list = new ArrayList<DynamicStringProperty>(); list.add(ACCESS_LOG); list.add(API_INTERCEPTOR_CONFIG); return list; } protected void prepareConfigFile() throws IOException { String config = API_INTERCEPTOR_CONFIG.get(); if (StringUtils.isBlank(config)) { new File(API_INTERCEPTOR_CONFIG_FILE.get()).delete(); } else { try(FileWriter fw = new FileWriter(API_INTERCEPTOR_CONFIG_FILE.get())) { IOUtils.write(API_INTERCEPTOR_CONFIG.get(), fw); } } } @Override protected boolean shouldRun() { return StringUtils.equals(HOST_API_PROXY_MODE_EMBEDDED, getHostApiProxyMode()); } @Override protected boolean isReady() { String host = ""; if (!clusterService.isMaster()) { ClusteredMember member = clusterService.getMaster(); if (member != null) { host = String.format("%s:%d", member.getAdvertiseAddress(), member.getHttpPort()); } } if (written.equals(host)) { return true; } try (FileWriter fw = new FileWriter(new File(MASTER_CONF + ".tmp"))) { IOUtils.write(host, fw); written = host; } catch (IOException e) { log.error("Failed to write configuration", e); return false; } return new File(MASTER_CONF + ".tmp").renameTo(new File(MASTER_CONF)); } @Override protected String binaryPath() { return "websocket-proxy"; } @Override protected void setEnvironment(Map<String, String> env) { env.clear(); String cattleProxyAddress = "localhost:" + getProxiedPort(); env.put("PATH", System.getenv("PATH")); env.put("PROXY_LISTEN_ADDRESS", ":" + getProxyPort()); env.put("PROXY_TLS_LISTEN_ADDRESS", ":" + getProxyPort()); env.put("PROXY_MASTER_FILE", MASTER_CONF); env.put("PROXY_CATTLE_ADDRESS", cattleProxyAddress); env.put("PROXY_HTTPS_PROXY_PROTOCOL_PORTS", getProxyProtocolHttpsPorts()); env.put("PROXY_API_INTERCEPTOR_CONFIG_FILE", API_INTERCEPTOR_CONFIG_FILE.get()); String processName = ManagementFactory.getRuntimeMXBean().getName(); if (processName != null) { String[] parts = processName.split("@"); if (parts.length > 0 && StringUtils.isNotEmpty(parts[0])) { env.put("PROXY_PARENT_PID", parts[0]); } } Credential cred = getCredential(); env.put("CATTLE_ACCESS_KEY", cred.getPublicValue()); env.put("CATTLE_SECRET_KEY", cred.getSecretValue()); } @Override protected void prepareProcess(ProcessBuilder pb) throws IOException { super.prepareProcess(pb); prepareConfigFile(); } @Override public void reload() { if (!shouldRun()) { return; } try { prepareConfigFile(); StringBuilder apiProxyUrl = new StringBuilder(); apiProxyUrl.append("http://localhost:").append(getProxyPort()).append("/v1-api-interceptor/reload"); Request.Post(apiProxyUrl.toString()).execute(); } catch (IOException e) { log.error("Failed to reload api proxy service: {}", e.getMessage()); } } @Override protected LockDefinition getLock() { return null; } private String getProxiedPort() { // To match the functionality in the Jetty Main class, need to get value this way as opposed // to using ArchaiusUtils String port = System.getenv("CATTLE_HTTP_PROXIED_PORT"); return port == null ? System.getProperty("cattle.http.proxied.port", "8081") : port; } private String getProxyPort() { // To match the functionality in the Jetty Main class, need to get value this way as opposed // to using ArchaiusUtils String port = System.getenv("CATTLE_HTTP_PORT"); return port == null ? System.getProperty("cattle.http.port", "8080") : port; } private String getProxyProtocolHttpsPorts() { String ports = System.getenv("PROXY_PROTOCOL_HTTPS_PORTS"); return ports == null ? System.getProperty("proxy.protocol.https.ports", "443") : ports; } }