installer: create namespaces with kube client
diff --git a/core/installer/cmd/app_manager.go b/core/installer/cmd/app_manager.go
index 73e2311..cff270e 100644
--- a/core/installer/cmd/app_manager.go
+++ b/core/installer/cmd/app_manager.go
@@ -68,7 +68,14 @@
 	if err != nil {
 		return err
 	}
-	m, err := installer.NewAppManager(installer.NewRepoIO(repo, signer))
+	kube, err := installer.NewOutOfClusterNamespaceCreator(rootFlags.kubeConfig)
+	if err != nil {
+		return err
+	}
+	m, err := installer.NewAppManager(
+		installer.NewRepoIO(repo, signer),
+		kube,
+	)
 	if err != nil {
 		return err
 	}
@@ -173,7 +180,7 @@
 	}
 	var resp rendered
 	resp.Readme = readme.String()
-	for _, tmpl := range a.Templates {
+	for _, tmpl := range a.Templates { // TODO(giolekva): deduplicate with Install
 		var f bytes.Buffer
 		if err := tmpl.Execute(&f, all); err != nil {
 			fmt.Printf("%+v\n", all)
@@ -207,7 +214,15 @@
 	if err != nil {
 		return err
 	}
-	if err := s.m.Install(*a, values); err != nil {
+	config, err := s.m.Config()
+	if err != nil {
+		return err
+	}
+	nsGen := installer.NewCombine(
+		installer.NewPrefixGenerator(config.Values.Id+"-"),
+		installer.NewRandomSuffixGenerator(3),
+	)
+	if err := s.m.Install(*a, nsGen, values); err != nil {
 		return err
 	}
 	return c.String(http.StatusOK, "Installed")
diff --git a/core/installer/cmd/bootstrap.go b/core/installer/cmd/bootstrap.go
index 135b6b4..ffff0ba 100644
--- a/core/installer/cmd/bootstrap.go
+++ b/core/installer/cmd/bootstrap.go
@@ -86,11 +86,11 @@
 	if err := installLonghorn(); err != nil {
 		return err
 	}
-	time.Sleep(5 * time.Minute) // TODO(giolekva): implement proper wait
+	time.Sleep(2 * time.Minute) // TODO(giolekva): implement proper wait
 	if err := installSoftServe(bootstrapJobKeys.Public); err != nil {
 		return err
 	}
-	time.Sleep(2 * time.Minute) // TODO(giolekva): implement proper wait
+	time.Sleep(1 * time.Minute) // TODO(giolekva): implement proper wait
 	ss, err := soft.NewClient(bootstrapFlags.softServeIP, 22, []byte(bootstrapJobKeys.Private), log.Default())
 	if err != nil {
 		return err
@@ -113,16 +113,20 @@
 	global := map[string]any{
 		"PCloudEnvName": bootstrapFlags.pcloudEnvName,
 	}
-	if err := installInfrastructureServices(repoIO, global); err != nil {
+	nsCreator, err := newNSCreator()
+	if err != nil {
 		return err
 	}
-	if err := installEnvManager(ss, repoIO, global); err != nil {
+	nsGen := installer.NewPrefixGenerator("pcloud-")
+	if err := installInfrastructureServices(repoIO, nsGen, nsCreator, global); err != nil {
+		return err
+	}
+	if err := installEnvManager(ss, repoIO, nsGen, nsCreator, global); err != nil {
 		return err
 	}
 	if ss.RemovePublicKey("admin", bootstrapJobKeys.Public); err != nil {
 		return err
 	}
-
 	return nil
 }
 
@@ -372,16 +376,33 @@
 	return nil
 }
 
-func installInfrastructureServices(repo installer.RepoIO, global map[string]any) error {
-	values := map[string]any{
-		"Global": global,
-	}
+func installInfrastructureServices(repo installer.RepoIO, nsGen installer.NamespaceGenerator, nsCreator installer.NamespaceCreator, global map[string]any) error {
 	appRepo := installer.NewInMemoryAppRepository(installer.CreateAllApps())
 	install := func(name string) error {
 		app, err := appRepo.Find(name)
 		if err != nil {
 			return err
 		}
+		namespaces := make([]string, len(app.Namespaces))
+		for i, n := range app.Namespaces {
+			namespaces[i], err = nsGen.Generate(n)
+			if err != nil {
+				return err
+			}
+		}
+		for _, n := range namespaces {
+			if err := nsCreator.Create(n); err != nil {
+				return err
+			}
+		}
+		values := map[string]any{
+			"Global": global,
+		}
+		if len(namespaces) > 0 {
+			values["Release"] = map[string]any{
+				"Namespace": namespaces[0],
+			}
+		}
 		return repo.InstallApp(*app, "infrastructure", values)
 	}
 	appsToInstall := []string{
@@ -445,7 +466,7 @@
 	return nil
 }
 
-func installEnvManager(ss *soft.Client, repo installer.RepoIO, global map[string]any) error {
+func installEnvManager(ss *soft.Client, repo installer.RepoIO, nsGen installer.NamespaceGenerator, nsCreator installer.NamespaceCreator, global map[string]any) error {
 	keys, err := installer.NewSSHKeyPair()
 	if err != nil {
 		return err
@@ -458,16 +479,31 @@
 		return err
 	}
 	appRepo := installer.NewInMemoryAppRepository(installer.CreateAllApps())
-	envManager, err := appRepo.Find("env-manager")
+	app, err := appRepo.Find("env-manager")
 	if err != nil {
 		return err
 	}
-	return repo.InstallApp(*envManager, "infrastructure", map[string]any{
+	namespaces := make([]string, len(app.Namespaces))
+	for i, n := range app.Namespaces {
+		namespaces[i], err = nsGen.Generate(n)
+		if err != nil {
+			return err
+		}
+	}
+	for _, n := range namespaces {
+		if err := nsCreator.Create(n); err != nil {
+			return err
+		}
+	}
+	return repo.InstallApp(*app, "infrastructure", map[string]any{
 		"Global": global,
 		"Values": map[string]any{
 			"RepoIP":        bootstrapFlags.softServeIP,
 			"SSHPrivateKey": keys.Private,
 		},
+		"Release": map[string]any{
+			"Namespace": namespaces[0],
+		},
 	})
 }
 
diff --git a/core/installer/cmd/env_manager.go b/core/installer/cmd/env_manager.go
index 647850d..a74369d 100644
--- a/core/installer/cmd/env_manager.go
+++ b/core/installer/cmd/env_manager.go
@@ -64,10 +64,15 @@
 		return err
 	}
 	repoIO := installer.NewRepoIO(repo, ss.Signer)
+	nsCreator, err := newNSCreator()
+	if err != nil {
+		return err
+	}
 	s := welcome.NewEnvServer(
 		envManagerFlags.port,
 		ss,
 		repoIO,
+		nsCreator,
 	)
 	s.Start()
 	return nil
diff --git a/core/installer/cmd/kube.go b/core/installer/cmd/kube.go
new file mode 100644
index 0000000..995aec3
--- /dev/null
+++ b/core/installer/cmd/kube.go
@@ -0,0 +1,13 @@
+package main
+
+import (
+	"github.com/giolekva/pcloud/core/installer"
+)
+
+func newNSCreator() (installer.NamespaceCreator, error) {
+	if rootFlags.kubeConfig != "" {
+		return installer.NewOutOfClusterNamespaceCreator(rootFlags.kubeConfig)
+	} else {
+		return installer.NewInClusterNamespaceCreator()
+	}
+}
diff --git a/core/installer/cmd/welcome.go b/core/installer/cmd/welcome.go
index 3b834b0..6d2d7e0 100644
--- a/core/installer/cmd/welcome.go
+++ b/core/installer/cmd/welcome.go
@@ -63,9 +63,14 @@
 		InsecureSkipTLS: true,
 		Progress:        os.Stdout,
 	})
+	nsCreator, err := newNSCreator()
+	if err != nil {
+		return err
+	}
 	s := welcome.NewServer(
 		welcomeFlags.port,
 		installer.NewRepoIO(repo, auth.Signer),
+		nsCreator,
 	)
 	s.Start()
 	return nil