Skip to content

Commit

Permalink
Rework singularity UI locations
Browse files Browse the repository at this point in the history
- move singularity UI away from the root resource to /ui location.
  This allows other resources to claim the root URI or add other endpoints
  based off the root URI

- Add a new flag in the ui section: redirectRootToUi: true / false

  If true, accessing the root URI of Singularity will redirect to the
  UI. If false, no redirection will happen (and whatever other
  resource is configured for / will kick in. By default this is 404
  (not found).

- move static and api-doc resources to /assets subfolder (removes the
  weird /static/static references)
  • Loading branch information
Henning Schmiedehausen committed Jan 26, 2015
1 parent 05a3cbf commit 03fce91
Show file tree
Hide file tree
Showing 17 changed files with 125 additions and 30 deletions.
2 changes: 1 addition & 1 deletion SingularityService/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@

<outputTemplate>${basedir}/../SingularityUI/app/assets/api-docs/index.html.mustache</outputTemplate>
<mustacheFileRoot>${basedir}/../SingularityUI/app/assets/api-docs</mustacheFileRoot>
<outputPath>${project.build.directory}/classes/static/api-docs/index.html</outputPath>
<outputPath>${project.build.directory}/classes/assets/api-docs/index.html</outputPath>
<sortApis>true</sortApis>
</apiSource>
</apiSources>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ public class SingularityMainModule implements Module {

public static final String HTTP_HOST_AND_PORT = "http.host.and.port";

public static final String SINGULARITY_URI_BASE = "_singularity_uri_base";

@Override
public void configure(Binder binder) {
binder.bind(HostAndPort.class).annotatedWith(named(HTTP_HOST_AND_PORT)).toProvider(SingularityHostAndPortProvider.class).in(Scopes.SINGLETON);
Expand Down Expand Up @@ -143,8 +145,13 @@ public static class SingularityHostAndPortProvider implements Provider<HostAndPo
public HostAndPort get() {
return HostAndPort.fromParts(hostname, httpPort);
}
}


@Provides
@Named(SINGULARITY_URI_BASE)
String getSingularityUriBase(final SingularityConfiguration configuration) {
final String singularityUiPrefix = configuration.getUiConfiguration().getBaseUrl().or(((SimpleServerFactory) configuration.getServerFactory()).getApplicationContextPath());
return (singularityUiPrefix.endsWith("/")) ? singularityUiPrefix.substring(0, singularityUiPrefix.length() - 1) : singularityUiPrefix;
}

@Provides
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ public void initialize(final Bootstrap<T> bootstrap) {

bootstrap.addBundle(new CorsBundle());
bootstrap.addBundle(new ViewBundle());
bootstrap.addBundle(new AssetsBundle("/static/static/", "/static/"));
bootstrap.addBundle(new AssetsBundle("/static/api-docs/", "/api-docs/", "index.html", "api-docs"));
bootstrap.addBundle(new AssetsBundle("/assets/static/", "/static/"));
bootstrap.addBundle(new AssetsBundle("/assets/api-docs/", "/api-docs/", "index.html", "api-docs"));
bootstrap.addBundle(new MigrationsBundle<SingularityConfiguration>() {
@Override
public DataSourceFactory getDataSourceFactory(final SingularityConfiguration configuration) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ protected void configure(Binder binder, SingularityConfiguration configuration)
binder.install(new SingularityMainModule());
binder.install(new SingularityDataModule());
binder.install(new SingularitySchedulerModule());
binder.install(new SingularityResourceModule());
binder.install(new SingularityResourceModule(configuration.getUiConfiguration()));
binder.install(new SingularityTranscoderModule());
binder.install(new SingularityHistoryModule(configuration));
binder.install(new SingularityMesosModule());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ public class UIConfiguration {
private boolean hideNewDeployButton = false;
private boolean hideNewRequestButton = false;

/**
* If true, the root of the server (http://.../singularity/) will open the UI. Otherwise,
* the UI URI (http://.../singularity/ui/) must be used.
*/
@JsonProperty
private boolean redirectRootToUi = true;

public boolean isHideNewDeployButton() {
return hideNewDeployButton;
}
Expand Down Expand Up @@ -63,4 +70,12 @@ public String getNavColor() {
public void setNavColor(String navColor) {
this.navColor = navColor;
}

public boolean isRedirectRootToUi() {
return redirectRootToUi;
}

public void setRedirectRootToUi(boolean redirectRootToUi) {
this.redirectRootToUi = redirectRootToUi;
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
package com.hubspot.singularity.resources;

import static com.hubspot.singularity.SingularityMainModule.SINGULARITY_URI_BASE;
import static com.hubspot.singularity.resources.UiResource.UI_RESOURCE_LOCATION;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;

import com.google.inject.Inject;
import com.hubspot.singularity.config.SingularityConfiguration;
import com.hubspot.singularity.views.IndexView;
import com.google.inject.name.Named;

@Path("/{wildcard:.*}")
@Path("/")
@Produces(MediaType.TEXT_HTML)
public class IndexResource {
private final SingularityConfiguration configuration;
private final String singularityUriBase;

@Inject
public IndexResource(SingularityConfiguration configuration) {
this.configuration = configuration;
public IndexResource(@Named(SINGULARITY_URI_BASE) String singularityUriBase) {
this.singularityUriBase = singularityUriBase;
}

@GET
public IndexView getIndex() {
return new IndexView(configuration);
@Path("/")
public Response getIndex(@Context UriInfo info) {
return Response.status(Status.MOVED_PERMANENTLY).location(UriBuilder.fromPath(singularityUriBase).path(UI_RESOURCE_LOCATION).build()).build();
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
package com.hubspot.singularity.resources;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.inject.AbstractModule;
import com.google.inject.Scopes;
import com.hubspot.singularity.config.UIConfiguration;
import com.hubspot.singularity.guice.GuicePropertyFilteringMessageBodyWriter;

public class SingularityResourceModule extends AbstractModule {

private final UIConfiguration uiConfiguration;

public SingularityResourceModule(UIConfiguration uiConfiguration) {
this.uiConfiguration = checkNotNull(uiConfiguration, "uiConfiguration is null");
}

@Override
protected void configure() {
bind(GuicePropertyFilteringMessageBodyWriter.class).in(Scopes.SINGLETON);
Expand All @@ -14,7 +23,6 @@ protected void configure() {
// not singletons, just in case.
bind(DeployResource.class);
bind(HistoryResource.class);
bind(IndexResource.class);
bind(RackResource.class);
bind(RequestResource.class);
bind(S3LogResource.class);
Expand All @@ -24,5 +32,10 @@ protected void configure() {
bind(TaskResource.class);
bind(TestResource.class);
bind(WebhookResource.class);
bind(UiResource.class);

if (uiConfiguration.isRedirectRootToUi()) {
bind(IndexResource.class);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.hubspot.singularity.resources;

import static com.hubspot.singularity.SingularityMainModule.SINGULARITY_URI_BASE;

import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import com.google.inject.Inject;
import com.google.inject.name.Named;
import com.hubspot.singularity.config.SingularityConfiguration;
import com.hubspot.singularity.views.IndexView;

/**
* Serves as the base for the UI, returns the mustache view for the actual GUI.
*/
@Singleton
@Path(UiResource.UI_RESOURCE_LOCATION + "{uiPath:.*}")
public class UiResource {

static final String UI_RESOURCE_LOCATION = "/ui/";

private final SingularityConfiguration configuration;
private final String singularityUriBase;

@Inject
public UiResource(@Named(SINGULARITY_URI_BASE) String singularityUriBase, SingularityConfiguration configuration) {
this.configuration = configuration;
this.singularityUriBase = singularityUriBase;
}

@GET
@Produces(MediaType.TEXT_HTML)
public IndexView getIndex() {
return new IndexView(singularityUriBase, UI_RESOURCE_LOCATION, configuration);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.hubspot.singularity.views;

import io.dropwizard.server.SimpleServerFactory;
import static com.google.common.base.Preconditions.checkNotNull;

import io.dropwizard.views.View;

import com.hubspot.singularity.SingularityService;
Expand All @@ -9,6 +10,7 @@
public class IndexView extends View {

private final String appRoot;
private final String apiDocs;
private final String staticRoot;
private final String apiRoot;
private final String navColor;
Expand All @@ -24,25 +26,28 @@ public class IndexView extends View {
private final Integer slaveHttpPort;
private final Integer slaveHttpsPort;

public IndexView(SingularityConfiguration configuration) {
public IndexView(String singularityUriBase, String appRoot, SingularityConfiguration configuration) {
super("index.mustache");

appRoot = configuration.getUiConfiguration().getBaseUrl().or(((SimpleServerFactory) configuration.getServerFactory()).getApplicationContextPath());
staticRoot = String.format("%s/static", appRoot);
apiRoot = String.format("%s%s", appRoot, SingularityService.API_BASE_PATH);
checkNotNull(singularityUriBase, "singularityUriBase is null");

this.appRoot = String.format("%s%s", singularityUriBase, appRoot);
this.staticRoot = String.format("%s/static", singularityUriBase);
this.apiDocs = String.format("%s/api-docs", singularityUriBase);
this.apiRoot = String.format("%s%s", singularityUriBase, SingularityService.API_BASE_PATH);

title = configuration.getUiConfiguration().getTitle();
this.title = configuration.getUiConfiguration().getTitle();

slaveHttpPort = configuration.getMesosConfiguration().getSlaveHttpPort();
slaveHttpsPort = configuration.getMesosConfiguration().getSlaveHttpsPort().orNull();
this.slaveHttpPort = configuration.getMesosConfiguration().getSlaveHttpPort();
this.slaveHttpsPort = configuration.getMesosConfiguration().getSlaveHttpsPort().orNull();

defaultCpus = configuration.getMesosConfiguration().getDefaultCpus();
defaultMemory = configuration.getMesosConfiguration().getDefaultMemory();
this.defaultCpus = configuration.getMesosConfiguration().getDefaultCpus();
this.defaultMemory = configuration.getMesosConfiguration().getDefaultMemory();

hideNewDeployButton = configuration.getUiConfiguration().isHideNewDeployButton();
hideNewRequestButton = configuration.getUiConfiguration().isHideNewRequestButton();
this.hideNewDeployButton = configuration.getUiConfiguration().isHideNewDeployButton();
this.hideNewRequestButton = configuration.getUiConfiguration().isHideNewRequestButton();

navColor = configuration.getUiConfiguration().getNavColor();
this.navColor = configuration.getUiConfiguration().getNavColor();
}

public String getAppRoot() {
Expand All @@ -53,6 +58,10 @@ public String getStaticRoot() {
return staticRoot;
}

public String getApiDocs() {
return apiDocs;
}

public String getApiRoot() {
return apiRoot;
}
Expand Down
1 change: 1 addition & 0 deletions SingularityUI/app/assets/_index.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<script>
window.config = {
appRoot: "{{{appRoot}}}",
apiDocs: "{{{apiDocs}}}",
apiRoot: localStorage.getItem("apiRootOverride") || "{{{apiRoot}}}",
title: "{{{title}}}",
hideNewDeployButton: {{{hideNewDeployButton}}},
Expand Down
3 changes: 3 additions & 0 deletions SingularityUI/app/handlebarsHelpers.coffee
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
Handlebars.registerHelper 'appRoot', ->
config.appRoot

Handlebars.registerHelper 'apiDocs', ->
config.apiDocs

Handlebars.registerHelper 'ifEqual', (v1, v2, options) ->
if v1 is v2 then options.fn @ else options.inverse @

Expand Down
2 changes: 1 addition & 1 deletion SingularityUI/app/templates/nav.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
</ul>
</li>
<li>
<a target="_blank" href="{{appRoot}}/api-docs/">API Docs</a>
<a target="_blank" href="{{apiDocs}}/">API Docs (beta)</a>
</li>
<li>
<a class='global-search-button'>
Expand Down
4 changes: 2 additions & 2 deletions SingularityUI/config.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ handlebars = require 'handlebars-brunch/node_modules/handlebars'
# Brunch settings
exports.config =
paths:
public: path.resolve(__dirname, '../SingularityService/target/generated-resources/static')
public: path.resolve(__dirname, '../SingularityService/target/generated-resources/assets')

files:
javascripts:
Expand Down Expand Up @@ -38,7 +38,7 @@ exports.config =

templateData =
staticRoot: "#{ @config.server.base }/static"
appRoot: @config.server.base
appRoot: "#{ @config.server.base }/ui"
apiRoot: ''
slaveHttpPort: 5051
title: 'Singularity (local dev)'
Expand Down

0 comments on commit 03fce91

Please sign in to comment.