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