Skip to content

Commit

Permalink
feat(global): add templates support ('apps') (portainer#154)
Browse files Browse the repository at this point in the history
  • Loading branch information
deviantony authored Aug 23, 2016
1 parent faccf2a commit 1c8aa35
Show file tree
Hide file tree
Showing 15 changed files with 366 additions and 13 deletions.
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
FROM scratch
FROM centurylink/ca-certs

COPY dist /

VOLUME /data

EXPOSE 9000

ENTRYPOINT ["/ui-for-docker"]
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,14 @@ server {

Replace `ADDRESS:PORT` with the CloudInovasi UI container details.

### Host your own apps

You can specify an URL to your own templates (**Apps**) definitions using the `--templates` or `-t` flags.

By default, CloudInovasi templates will be used (https://raw.githubusercontent.com/cloud-inovasi/ui-templates/master/templates.json).

For more information about hosting your own template definition and the format, see: https://github.com/cloud-inovasi/ui-templates

### Available options

The following options are available for the `ui-for-docker` binary:
Expand All @@ -157,3 +165,4 @@ The following options are available for the `ui-for-docker` binary:
* `--tlskey`: Path to the TLS key (default `/certs/key.pem`)
* `--hide-label`, `-l`: Hide containers with a specific label in the UI
* `--logo`: URL to a picture to be displayed as a logo in the UI
* `--templates`, `-t`: URL to templates (apps) definitions
23 changes: 13 additions & 10 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ import (

type (
api struct {
endpoint *url.URL
bindAddress string
assetPath string
dataPath string
tlsConfig *tls.Config
endpoint *url.URL
bindAddress string
assetPath string
dataPath string
tlsConfig *tls.Config
templatesURL string
}

apiConfig struct {
Expand All @@ -26,6 +27,7 @@ type (
TLSCACertPath string
TLSCertPath string
TLSKeyPath string
TemplatesURL string
}
)

Expand All @@ -48,10 +50,11 @@ func newAPI(apiConfig apiConfig) *api {
}

return &api{
endpoint: endpointURL,
bindAddress: apiConfig.BindAddress,
assetPath: apiConfig.AssetPath,
dataPath: apiConfig.DataPath,
tlsConfig: tlsConfig,
endpoint: endpointURL,
bindAddress: apiConfig.BindAddress,
assetPath: apiConfig.AssetPath,
dataPath: apiConfig.DataPath,
tlsConfig: tlsConfig,
templatesURL: apiConfig.TemplatesURL,
}
}
3 changes: 3 additions & 0 deletions api/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ func (a *api) newHandler(settings *Settings) http.Handler {
mux.HandleFunc("/settings", func(w http.ResponseWriter, r *http.Request) {
settingsHandler(w, r, settings)
})
mux.HandleFunc("/templates", func(w http.ResponseWriter, r *http.Request) {
templatesHandler(w, r, a.templatesURL)
})
return CSRFHandler(newCSRFWrapper(mux))
}

Expand Down
2 changes: 2 additions & 0 deletions api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func main() {
swarm = kingpin.Flag("swarm", "Swarm cluster support").Default("false").Short('s').Bool()
labels = pairs(kingpin.Flag("hide-label", "Hide containers with a specific label in the UI").Short('l'))
logo = kingpin.Flag("logo", "URL for the logo displayed in the UI").String()
templates = kingpin.Flag("templates", "URL to the templates (apps) definitions").Default("https://raw.githubusercontent.com/cloud-inovasi/ui-templates/master/templates.json").Short('t').String()
)
kingpin.Parse()

Expand All @@ -32,6 +33,7 @@ func main() {
TLSCACertPath: *tlscacert,
TLSCertPath: *tlscert,
TLSKeyPath: *tlskey,
TemplatesURL: *templates,
}

settings := &Settings{
Expand Down
2 changes: 1 addition & 1 deletion api/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type Settings struct {
Logo string `json:"logo"`
}

// configurationHandler defines a handler function used to encode the configuration in JSON
// settingsHandler defines a handler function used to encode the configuration in JSON
func settingsHandler(w http.ResponseWriter, r *http.Request, s *Settings) {
json.NewEncoder(w).Encode(*s)
}
27 changes: 27 additions & 0 deletions api/templates.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package main

import (
"fmt"
"io/ioutil"
"log"
"net/http"
)

// templatesHandler defines a handler function used to retrieve the templates from a URL and put them in the response
func templatesHandler(w http.ResponseWriter, r *http.Request, templatesURL string) {
resp, err := http.Get(templatesURL)
if err != nil {
http.Error(w, fmt.Sprintf("Error making request to %s: %s", templatesURL, err.Error()), http.StatusInternalServerError)
log.Print(err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
http.Error(w, "Error reading body from templates URL", http.StatusInternalServerError)
log.Print(err)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(body)
}
7 changes: 7 additions & 0 deletions app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ angular.module('uifordocker', [
'network',
'networks',
'createNetwork',
'templates',
'volumes',
'createVolume'])
.config(['$stateProvider', '$urlRouterProvider', '$httpProvider', function ($stateProvider, $urlRouterProvider, $httpProvider) {
Expand Down Expand Up @@ -118,6 +119,11 @@ angular.module('uifordocker', [
templateUrl: 'app/components/network/network.html',
controller: 'NetworkController'
})
.state('templates', {
url: '/templates/',
templateUrl: 'app/components/templates/templates.html',
controller: 'TemplatesController'
})
.state('volumes', {
url: '/volumes/',
templateUrl: 'app/components/volumes/volumes.html',
Expand Down Expand Up @@ -156,4 +162,5 @@ angular.module('uifordocker', [
.constant('DOCKER_ENDPOINT', 'dockerapi')
.constant('DOCKER_PORT', '') // Docker port, leave as an empty string if no port is requred. If you have a port, prefix it with a ':' i.e. :4243
.constant('CONFIG_ENDPOINT', 'settings')
.constant('TEMPLATES_ENDPOINT', 'templates')
.constant('UI_VERSION', 'v1.7.0');
76 changes: 76 additions & 0 deletions app/components/templates/templates.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<rd-header>
<rd-header-title title="Apps list">
<a data-toggle="tooltip" title="Refresh" ui-sref="templates" ui-sref-opts="{reload: true}">
<i class="fa fa-refresh" aria-hidden="true"></i>
</a>
</rd-header-title>
<rd-header-content>Apps</rd-header-content>
</rd-header>

<div class="row">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-header icon="fa-rocket" title="Available apps">
<div class="pull-right">
<i id="loadTemplatesSpinner" class="fa fa-cog fa-2x fa-spin" style="margin-top: 5px;"></i>
</div>
</rd-widget-header>
<rd-widget-body classes="padding">
<div class="template-list">
<div ng-repeat="tpl in templates" class="container-template hvr-grow" id="template_{{ $index }}" ng-click="selectTemplate($index)">
<img class="logo" ng-src="{{ tpl.logo }}" />
<div class="title">{{ tpl.title }}</div>
<div class="comment">{{ tpl.comment }}</div>
</div>
</div>
</rd-widget-body>
</rd-widget>
</div>
</div>

<div class="row" ng-if="selectedTemplate">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-header icon="fa-cogs" title="Configuration"></rd-widget-header>
<rd-widget-body classes="padding">
<form class="form-horizontal">
<div class="form-group" ng-if="globalNetworkCount === 0">
<div class="col-sm-12">
<span class="small text-muted">When using Swarm, we recommend deploying containers in a shared network. Looks like you don't have any shared network, head over the <a ui-sref="networks">networks view</a> to create one.</span>
</div>
</div>
<!-- name-and-network-inputs -->
<div class="form-group">
<label for="image_registry" class="col-sm-2 control-label text-left">Name</label>
<div class="col-sm-4">
<input type="text" class="form-control" ng-model="formValues.name" placeholder="e.g. web (optional)">
</div>
<label for="container_network" class="col-sm-2 control-label text-right">Network</label>
<div class="col-sm-4">
<select class="selectpicker form-control" ng-model="formValues.network">
<option selected disabled hidden value="">Select a network</option>
<option ng-repeat="net in availableNetworks" ng-value="net.Name">{{ net.Name }}</option>
</select>
</div>
</div>
<!-- !name-and-network-inputs -->
<div ng-repeat="var in selectedTemplate.env" class="form-group">
<label for="field_{{ $index }}" class="col-sm-2 control-label text-left">{{ var.label }}</label>
<div class="col-sm-10">
<input type="text" class="form-control" ng-model="var.value" id="field_{{ $index }}">
</div>
</div>
</form>
</rd-widget-body>
</rd-widget>
</div>
</div>

<div class="row" ng-if="selectedTemplate">
<div class="col-lg-12 col-md-12 col-xs-12" style="text-align: center;">
<div>
<i id="createContainerSpinner" class="fa fa-cog fa-3x fa-spin" style="margin-bottom: 5px; display: none;"></i>
</div>
<button type="button" class="btn btn-default btn-lg" ng-disabled="!formValues.network" ng-click="createTemplate()">Create</button>
</div>
</div>
Loading

0 comments on commit 1c8aa35

Please sign in to comment.