appmanager: move pcloud configs into templates
diff --git a/appmanager/cmd/main.go b/appmanager/cmd/main.go
index b4ef21a..d4205d2 100644
--- a/appmanager/cmd/main.go
+++ b/appmanager/cmd/main.go
@@ -174,27 +174,30 @@
 	if err != nil {
 		return err
 	}
+	if err := h.Render(
+		*helmBin,
+		map[string]string{}); err != nil {
+		return err
+	}
+	glog.Info("Rendered templates")
 	if err = app.InstallSchema(h.Schema, *apiAddr); err != nil {
 		return err
 	}
 	glog.Infof("Installed schema: %s", h.Schema)
-	namespace := fmt.Sprintf("app-%s", h.Name)
-	err = createNamespace(hn.client.CoreV1().Namespaces(), namespace)
+	err = createNamespace(hn.client.CoreV1().Namespaces(), h.Namespace)
 	if err != nil {
 		return err
 	}
-	glog.Infof("Created namespaces: %s", namespace)
+	glog.Infof("Created namespaces: %s", h.Namespace)
 	if h.Type == "application" {
-		if err = h.Install(
-			*helmBin,
-			map[string]string{}); err != nil {
+		if err := h.Install(*helmBin); err != nil {
 			return err
 		}
 		glog.Info("Deployed")
 	} else {
 		glog.Info("Skipping deployment as we got library chart.")
 	}
-	hn.manager.Apps[h.Name] = app.App{h.Name, namespace, h.Triggers, h.Actions}
+	hn.manager.Apps[h.Name] = app.App{h.Name, h.Namespace, h.Triggers, h.Actions}
 	app.StoreManagerStateToFile(hn.manager, *managerStoreFile)
 	for _, a := range h.Init.PostInstall.CallAction {
 		if err := hn.launchAction(actionReq{a.App, a.Action, a.Args}); err != nil {
diff --git a/appmanager/helm.go b/appmanager/helm.go
index c8c2730..894c96c 100644
--- a/appmanager/helm.go
+++ b/appmanager/helm.go
@@ -3,16 +3,13 @@
 import (
 	"errors"
 	"fmt"
-	"io/ioutil"
 	"os"
 	"os/exec"
 	"path"
 	"path/filepath"
 	"strings"
-	"syscall"
 
 	"github.com/golang/glog"
-	"gopkg.in/yaml.v2"
 )
 
 type Chart struct {
@@ -22,47 +19,71 @@
 
 type HelmChart struct {
 	Chart
-	chartDir string
-	Schema   Schema
-	Triggers Triggers
-	Actions  Actions
-	Init     Init
-	Yamls    []string
+	Dir       string
+	Namespace string
+	Schema    Schema
+	Triggers  Triggers
+	Actions   Actions
+	Init      Init
+	Yamls     []string
 }
 
-func HelmChartFromDir(chartDir string) (*HelmChart, error) {
+func HelmChartFromDir(dir string) (*HelmChart, error) {
 	var chart HelmChart
-	chart.chartDir = chartDir
-	err := FromYamlFile(path.Join(chartDir, "Chart.yaml"), &chart.Chart)
+	chart.Dir = dir
+	err := FromYamlFile(path.Join(dir, "Chart.yaml"), &chart.Chart)
 	if err != nil {
 		return nil, err
 	}
-	err = FromYamlFile(path.Join(chartDir, "Schema.yaml"), &chart.Schema)
-	if err != nil && !os.IsNotExist(err) {
-		return nil, err
-	}
-	err = FromYamlFile(path.Join(chartDir, "Triggers.yaml"), &chart.Triggers)
-	if err != nil && !os.IsNotExist(err) {
-		return nil, err
-	}
-	err = FromYamlFile(path.Join(chartDir, "Actions.yaml"), &chart.Actions)
-	if err != nil && !os.IsNotExist(err) {
-		return nil, err
-	}
-	err = FromYamlFile(path.Join(chartDir, "Init.yaml"), &chart.Init)
-	if err != nil && !os.IsNotExist(err) {
-		return nil, err
-	}
 	return &chart, nil
 }
 
+func (chart *HelmChart) Render(
+	helmBin string,
+	values map[string]string) error {
+	chart.Namespace = fmt.Sprintf("app-%s", chart.Name)
+	renderDir := path.Join(chart.Dir, "__render")
+	if err := chart.renderTemplates(helmBin, values, renderDir); err != nil {
+		return err
+	}
+	if err := os.RemoveAll(path.Join(chart.Dir, "templates")); err != nil {
+		return err
+	}
+	if err := os.Rename(
+		path.Join(renderDir, chart.Name, "templates"),
+		path.Join(chart.Dir, "templates")); err != nil {
+		return err
+	}
+	pcloudDir := path.Join(chart.Dir, "templates/pcloud")
+	err := FromYamlFile(path.Join(pcloudDir, "Schema.yaml"), &chart.Schema)
+	if err != nil && !os.IsNotExist(err) {
+		return err
+	}
+	err = FromYamlFile(path.Join(pcloudDir, "Triggers.yaml"), &chart.Triggers)
+	if err != nil && !os.IsNotExist(err) {
+		return err
+	}
+	err = FromYamlFile(path.Join(pcloudDir, "Actions.yaml"), &chart.Actions)
+	if err != nil && !os.IsNotExist(err) {
+		return err
+	}
+	err = FromYamlFile(path.Join(pcloudDir, "Init.yaml"), &chart.Init)
+	if err != nil && !os.IsNotExist(err) {
+		return err
+	}
+	if err := os.RemoveAll(pcloudDir); err != nil {
+		return err
+	}
+	return nil
+}
+
 func HelmChartFromTar(chartTar string) (*HelmChart, error) {
 	if !strings.HasSuffix(chartTar, ".tar.gz") {
 		return nil, errors.New("Expected .tar.gz file")
 	}
 	dir := filepath.Dir(chartTar)
 	archive := filepath.Base(chartTar)
-	if err := syscall.Chdir(dir); err != nil {
+	if err := os.Chdir(dir); err != nil {
 		return nil, err
 	}
 	cmd := exec.Command("tar", "-xvf", archive)
@@ -78,11 +99,34 @@
 	return HelmChartFromDir(dir)
 }
 
-func (h *HelmChart) Install(
+func (chart *HelmChart) Install(
+	helmBin string) error {
+	cmd := exec.Command(helmBin)
+	cmd.Args = append(cmd.Args, "install")
+	cmd.Args = append(cmd.Args, fmt.Sprintf("--namespace=%s", chart.Namespace))
+	cmd.Args = append(cmd.Args, chart.Name)
+	cmd.Args = append(cmd.Args, fmt.Sprintf("%s", chart.Dir))
+	return runCmd(cmd)
+}
+
+func (chart *HelmChart) renderTemplates(
 	helmBin string,
-	values map[string]string) error {
-	namespace := fmt.Sprintf("app-%s", h.Chart.Name)
-	cmd := generateHelmInstallCmd(helmBin, h.chartDir, namespace, values)
+	values map[string]string,
+	outputDir string) error {
+	cmd := exec.Command(helmBin)
+	cmd.Args = append(cmd.Args, "template")
+	cmd.Args = append(cmd.Args, fmt.Sprintf("--output-dir=%s", outputDir))
+	cmd.Args = append(cmd.Args, fmt.Sprintf("--namespace=%s", chart.Namespace))
+	cmd.Args = append(cmd.Args, chart.Name)
+	cmd.Args = append(cmd.Args, chart.Dir)
+	// TODO(giolekva): validate values
+	for key, value := range values {
+		cmd.Args = append(cmd.Args, fmt.Sprintf("--set=%s=%s", key, value))
+	}
+	return runCmd(cmd)
+}
+
+func runCmd(cmd *exec.Cmd) error {
 	glog.Info(cmd.String())
 	var stdout strings.Builder
 	var stderr strings.Builder
@@ -95,42 +139,3 @@
 	glog.Info(stdout.String())
 	return nil
 }
-
-func generateHelmInstallCmd(
-	helmBin string,
-	archive string,
-	namespace string,
-	values map[string]string) *exec.Cmd {
-	cmd := exec.Command(helmBin)
-	cmd.Args = append(cmd.Args, "install")
-	cmd.Args = append(cmd.Args, fmt.Sprintf("--namespace=%s", namespace))
-	cmd.Args = append(cmd.Args, "--generate-name")
-	cmd.Args = append(cmd.Args, fmt.Sprintf("%s", archive))
-	// TODO(giolekva): validate values
-	for key, value := range values {
-		cmd.Args = append(cmd.Args, fmt.Sprintf("--set=%s=%s", key, value))
-	}
-	return cmd
-}
-
-func ChartFromYaml(str string) (*Chart, error) {
-	var s Chart
-	err := yaml.Unmarshal([]byte(str), &s)
-	if err != nil {
-		return nil, err
-	}
-	return &s, nil
-}
-
-func ReadChart(chartFile string) (*Chart, error) {
-	f, err := os.Open(chartFile)
-	if err != nil {
-		return nil, err
-	}
-	defer f.Close()
-	b, err := ioutil.ReadAll(f)
-	if err != nil {
-		return nil, err
-	}
-	return ChartFromYaml(string(b))
-}
diff --git a/appmanager/launcher.go b/appmanager/launcher.go
index aeed5b7..e1cec0b 100644
--- a/appmanager/launcher.go
+++ b/appmanager/launcher.go
@@ -13,6 +13,9 @@
 	"github.com/golang/glog"
 )
 
+var leftDelim = "{-{"
+var rightDelim = "}-}"
+
 type Launcher interface {
 	Launch(ns, tmpl string, args map[string]interface{}) error
 }
@@ -40,7 +43,7 @@
 }
 
 func renderTemplate(tmpl string, args map[string]interface{}) (*apiv1.Pod, error) {
-	t, err := template.New("action").Parse(tmpl)
+	t, err := template.New("action").Delims(leftDelim, rightDelim).Parse(tmpl)
 	if err != nil {
 		return nil, err
 	}
diff --git a/appmanager/main b/appmanager/main
deleted file mode 100755
index 9ad7e7c..0000000
--- a/appmanager/main
+++ /dev/null
Binary files differ
diff --git a/apps/face-detection/chart/Schema.yaml b/apps/face-detection/chart/templates/pcloud/Schema.yaml
similarity index 100%
rename from apps/face-detection/chart/Schema.yaml
rename to apps/face-detection/chart/templates/pcloud/Schema.yaml
diff --git a/apps/face-detection/chart/Triggers.yaml b/apps/face-detection/chart/templates/pcloud/Triggers.yaml
similarity index 100%
rename from apps/face-detection/chart/Triggers.yaml
rename to apps/face-detection/chart/templates/pcloud/Triggers.yaml
diff --git a/apps/minio/chart/Actions.yaml b/apps/minio/chart/templates/pcloud/Actions.yaml
similarity index 77%
rename from apps/minio/chart/Actions.yaml
rename to apps/minio/chart/templates/pcloud/Actions.yaml
index c754c5f..3d674a6 100644
--- a/apps/minio/chart/Actions.yaml
+++ b/apps/minio/chart/templates/pcloud/Actions.yaml
@@ -29,5 +29,5 @@
               name: minio-creds
               key: secretkey
         command: ["/bin/sh", "-c"]
-        args: ["mc config host add minio $MINIO_ADDR $ACCESS_KEY $SECRET_KEY && mc mb minio/{{ .BucketName }} && mc admin config set minio notify_webhook:minio queue_limit=\"1000\" queue_dir=\"/tmp/events\" endpoint=\"{{ .WebhookURL }}\" && mc admin service restart minio && mc event add minio/{{ .BucketName }} arn:minio:sqs::minio:webhook --event put && mc policy set public minio/{{ .BucketName }}"]
+        args: ["mc config host add minio $MINIO_ADDR $ACCESS_KEY $SECRET_KEY && mc mb minio/[[ .BucketName ]] && mc admin config set minio notify_webhook:minio queue_limit=\"1000\" queue_dir=\"/tmp/events\" endpoint=\"[[ .WebhookURL ]]\" && mc admin service restart minio && mc event add minio/[[ .BucketName ]] arn:minio:sqs::minio:webhook --event put && mc policy set public minio/[[ .BucketName ]]"]
       restartPolicy: Never
diff --git a/apps/minio/importer/chart/Init.yaml b/apps/minio/importer/chart/Init.yaml
deleted file mode 100644
index 9844b51..0000000
--- a/apps/minio/importer/chart/Init.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-postInstall:
-  callAction:
-  - app: object-store
-    action: create-bucket-with-webhook
-    args: 
-      WebhookURL: http://minio-importer.app-minio-importer.svc/new_object
-       # http://{{ .Chart.Name }}.{{ .Release.Namespace }}.svc:{{ .Values.servicePort }}/new_object
-      BucketName: images
\ No newline at end of file
diff --git a/apps/minio/importer/chart/templates/pcloud/Init.yaml b/apps/minio/importer/chart/templates/pcloud/Init.yaml
new file mode 100644
index 0000000..40d28d0
--- /dev/null
+++ b/apps/minio/importer/chart/templates/pcloud/Init.yaml
@@ -0,0 +1,7 @@
+postInstall:
+  callAction:
+  - app: object-store
+    action: create-bucket-with-webhook
+    args: 
+      WebhookURL: http://{{ .Chart.Name }}.{{ .Release.Namespace }}.svc:{{ .Values.servicePort }}/new_object
+      BucketName: images
\ No newline at end of file
diff --git a/apps/minio/importer/chart/Schema.yaml b/apps/minio/importer/chart/templates/pcloud/Schema.yaml
similarity index 100%
rename from apps/minio/importer/chart/Schema.yaml
rename to apps/minio/importer/chart/templates/pcloud/Schema.yaml