package io.fathom.auto.haproxy; import io.fathom.auto.JsonCodec; import io.fathom.auto.config.SecretKeys; import io.fathom.auto.haproxy.model.Backend; import io.fathom.auto.haproxy.model.Frontend; import io.fathom.auto.haproxy.template.BackendTemplate; import io.fathom.auto.haproxy.template.FrontendTemplate; import io.fathom.auto.haproxy.template.HeaderTemplate; import io.fathom.cloud.openstack.client.loadbalance.model.LbaasMapping; import io.fathom.cloud.openstack.client.loadbalance.model.LoadBalanceMappingList; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.net.URLEncoder; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Charsets; import com.google.common.base.Objects; import com.google.common.collect.Maps; import com.google.common.io.Files; public class HaproxyConfigBuilder { private static final String DEFAULT_FRONTEND_KEY = "_default"; private static final String DEFAULT_HOST_KEY = "_default"; private static final Logger log = LoggerFactory.getLogger(HaproxyConfigBuilder.class); private final File keysDir; private final File mirrorDir; private final SecretKeys secretKeys; private String defaultHost; public HaproxyConfigBuilder(File mirrorDir, File keysDir, SecretKeys secretKeys) { this.mirrorDir = mirrorDir; this.keysDir = keysDir; this.secretKeys = secretKeys; } public void visitDir(File dir) throws IOException { for (File file : dir.listFiles()) { if (!file.isFile()) { log.warn("Expected file: {}", file); continue; } visitFile(file); } } private void visitFile(File file) throws IOException { String json = Files.toString(file, Charsets.UTF_8); log.debug("Reading file: {}", file); LoadBalanceMappingList chunk = JsonCodec.gson.fromJson(json, LoadBalanceMappingList.class); if (chunk.mappings != null) { for (LbaasMapping mapping : chunk.mappings) { log.debug("Found mapping: {}", mapping); Backend backend = buildBackend(mapping); backend.mappings.add(mapping); } } } private Backend buildBackend(LbaasMapping mapping) throws IOException { String host = mapping.host; String key; if (host == null) { key = DEFAULT_HOST_KEY; } else { key = buildKey(host); } Backend backend = backends.get(key); if (backend != null) { return backend; } backend = new Backend(); backend.host = host; backend.key = key; backends.put(backend.key, backend); SecretKeys.SecretInfo secret; if (host != null) { secret = secretKeys.findSecret(host); } else if (defaultHost != null) { log.info("Checking secret for default host: " + defaultHost); secret = secretKeys.findSecret(defaultHost); } else { secret = null; } if (secret != null) { // TODO: Check if secret changed?? String s = secret.read(); File secretFile = new File(keysDir, secret.getId()); Files.write(s, secretFile, Charsets.UTF_8); backend.sslKey = secretFile.getAbsolutePath(); } String frontendKey = DEFAULT_FRONTEND_KEY; Frontend frontend = getFrontend(frontendKey); frontend.backends.add(backend); if (backend.sslKey != null) { if (frontend.sslKey == null) { frontend.sslKey = backend.sslKey; } else if (!Objects.equal(frontend.sslKey, backend.sslKey)) { log.warn("Arbitrarily choosing frontend sslKey"); } } return backend; } private Frontend getFrontend(String frontendKey) { Frontend frontend = frontends.get(frontendKey); if (frontend == null) { frontend = new Frontend(); frontend.key = frontendKey; frontends.put(frontendKey, frontend); } return frontend; } public File getMirrorDir() { return mirrorDir; } final Map<String, Backend> backends = Maps.newHashMap(); final Map<String, Frontend> frontends = Maps.newHashMap(); static String buildKey(String s) { String escaped; try { escaped = URLEncoder.encode(s, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new IllegalStateException("Cannot find UTF-8 encoding", e); } // escaped = escaped.replace('%', '_'); return escaped; } public void generateConfig(Writer writer) throws IOException { if (frontends.size() > 1) { throw new IllegalStateException(); } if (frontends.size() == 0) { getFrontend(DEFAULT_FRONTEND_KEY); } FrontendTemplate frontendTemplate = new FrontendTemplate(); BackendTemplate backendTemplate = new BackendTemplate(); HeaderTemplate headerTemplate = new HeaderTemplate(); headerTemplate.write(writer, null); for (Frontend frontend : frontends.values()) { frontendTemplate.write(writer, frontend); } for (Backend backend : backends.values()) { backendTemplate.write(writer, backend); } } public String getDefaultHost() { return defaultHost; } public void setDefaultHost(String defaultHost) { this.defaultHost = defaultHost; } }