installer: clean up codebase
* introduce helper soft package to work with SoftServe
* move commands to cmd/
diff --git a/core/installer/cmd/apps.go b/core/installer/cmd/apps.go
new file mode 100644
index 0000000..eb49736
--- /dev/null
+++ b/core/installer/cmd/apps.go
@@ -0,0 +1,77 @@
+package main
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ "github.com/giolekva/pcloud/core/installer"
+ "github.com/spf13/cobra"
+ "sigs.k8s.io/yaml"
+)
+
+var installFlags struct {
+ config string
+ appName string
+ outputDir string
+}
+
+func installCmd() *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "install",
+ RunE: installCmdRun,
+ }
+ cmd.Flags().StringVar(
+ &installFlags.config,
+ "config",
+ "",
+ "",
+ )
+ cmd.Flags().StringVar(
+ &installFlags.appName,
+ "app",
+ "",
+ "",
+ )
+ cmd.Flags().StringVar(
+ &installFlags.outputDir,
+ "output-dir",
+ "",
+ "",
+ )
+ return cmd
+}
+
+func installCmdRun(cmd *cobra.Command, args []string) error {
+ cfg, err := readConfig(installFlags.config)
+ if err != nil {
+ return err
+ }
+ apps := installer.CreateAllApps()
+ for _, a := range apps {
+ if a.Name == installFlags.appName {
+ for _, t := range a.Templates {
+ out, err := os.Create(filepath.Join(installFlags.outputDir, t.Name()))
+ if err != nil {
+ return err
+ }
+ defer out.Close()
+ if err := t.Execute(out, cfg); err != nil {
+ return err
+ }
+ }
+ break
+ }
+ }
+ return nil
+}
+
+func readConfig(config string) (installer.Config, error) {
+ var cfg installer.Config
+ inp, err := ioutil.ReadFile(config)
+ if err != nil {
+ return cfg, err
+ }
+ err = yaml.UnmarshalStrict(inp, &cfg)
+ return cfg, err
+}
diff --git a/core/installer/cmd/bootstrap.go b/core/installer/cmd/bootstrap.go
new file mode 100644
index 0000000..151dd90
--- /dev/null
+++ b/core/installer/cmd/bootstrap.go
@@ -0,0 +1,199 @@
+package main
+
+import (
+ "context"
+ "crypto/ed25519"
+ "crypto/rand"
+ "crypto/x509"
+ _ "embed"
+ "encoding/pem"
+ "fmt"
+ "golang.org/x/crypto/ssh"
+ "log"
+ "os"
+ "path/filepath"
+ "time"
+
+ "github.com/giolekva/pcloud/core/installer/soft"
+ "github.com/spf13/cobra"
+ "helm.sh/helm/v3/pkg/action"
+ "helm.sh/helm/v3/pkg/chart/loader"
+ "helm.sh/helm/v3/pkg/kube"
+)
+
+var bootstrapFlags struct {
+ chartsDir string
+ adminPubKey string
+ adminPrivKey string
+}
+
+func bootstrapCmd() *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "bootstrap",
+ RunE: bootstrapCmdRun,
+ }
+ cmd.Flags().StringVar(
+ &bootstrapFlags.chartsDir,
+ "charts-dir",
+ "",
+ "",
+ )
+ cmd.Flags().StringVar(
+ &bootstrapFlags.adminPubKey,
+ "admin-pub-key",
+ "",
+ "",
+ )
+ cmd.Flags().StringVar(
+ &bootstrapFlags.adminPrivKey,
+ "admin-priv-key",
+ "",
+ "",
+ )
+ return cmd
+}
+
+func bootstrapCmdRun(cmd *cobra.Command, args []string) error {
+ adminPubKey, adminPrivKey, err := readAdminKeys()
+ if err != nil {
+ return err
+ }
+ fluxPub, fluxPriv, err := generateSSHKeys()
+ if err != nil {
+ return err
+ }
+ softServePub, softServePriv, err := generateSSHKeys()
+ if err != nil {
+ return err
+ }
+ fmt.Println("Installing SoftServe")
+ if err := installSoftServe(softServePub, softServePriv, string(adminPubKey)); err != nil {
+ return err
+ }
+ time.Sleep(30 * time.Second)
+ ss, err := soft.NewClient("192.168.0.208", 22, adminPrivKey, log.Default())
+ if err != nil {
+ return err
+ }
+ if err := ss.UpdateConfig(
+ soft.DefaultConfig([]string{string(adminPubKey), fluxPub}),
+ "set admin keys"); err != nil {
+ return err
+ }
+ if err := ss.ReloadConfig(); err != nil {
+ return err
+ }
+ fmt.Println("Creating /pcloud repo")
+ if err := ss.AddRepository("pcloud", "# PCloud Systems\n"); err != nil {
+ return err
+ }
+ fmt.Println("Installing Flux")
+ if err := installFlux("ssh://soft-serve.pcloud.svc.cluster.local:22/pcloud", "soft-serve.pcloud.svc.cluster.local", softServePub, fluxPriv); err != nil {
+ return err
+ }
+ return nil
+}
+
+func installSoftServe(pubKey, privKey, adminKey string) error {
+ config, err := createActionConfig()
+ if err != nil {
+ return err
+ }
+ chart, err := loader.Load(filepath.Join(bootstrapFlags.chartsDir, "soft-serve"))
+ if err != nil {
+ return err
+ }
+ values := map[string]interface{}{
+ "privateKey": privKey,
+ "publicKey": pubKey,
+ "adminKey": adminKey,
+ }
+ installer := action.NewInstall(config)
+ installer.Namespace = "pcloud"
+ installer.CreateNamespace = true
+ installer.ReleaseName = "soft-serve"
+ installer.Wait = true
+ installer.Timeout = 5 * time.Minute
+ if _, err := installer.RunWithContext(context.TODO(), chart, values); err != nil {
+ return err
+ }
+ return nil
+}
+
+func installFlux(repoAddr, repoHost, repoHostPubKey, privateKey string) error {
+ config, err := createActionConfig()
+ if err != nil {
+ return err
+ }
+ chart, err := loader.Load(filepath.Join(bootstrapFlags.chartsDir, "flux-bootstrap"))
+ if err != nil {
+ return err
+ }
+ values := map[string]interface{}{
+ "repositoryAddress": repoAddr,
+ "repositoryHost": repoHost,
+ "repositoryHostPublicKey": repoHostPubKey,
+ "privateKey": privateKey,
+ }
+ installer := action.NewInstall(config)
+ installer.Namespace = "pcloud"
+ installer.CreateNamespace = true
+ installer.ReleaseName = "flux"
+ installer.Wait = true
+ installer.WaitForJobs = true
+ installer.Timeout = 5 * time.Minute
+ if _, err := installer.RunWithContext(context.TODO(), chart, values); err != nil {
+ return err
+ }
+ return nil
+}
+
+func createActionConfig() (*action.Configuration, error) {
+ config := new(action.Configuration)
+ if err := config.Init(
+ kube.GetConfig(rootFlags.kubeConfig, "", ""),
+ "pcloud",
+ "",
+ func(fmtString string, args ...interface{}) {
+ fmt.Printf(fmtString, args...)
+ fmt.Println()
+ },
+ ); err != nil {
+ return nil, err
+ }
+ return config, nil
+}
+
+func generateSSHKeys() (string, string, error) {
+ pub, priv, err := ed25519.GenerateKey(rand.Reader)
+ if err != nil {
+ return "", "", err
+ }
+ privEnc, err := x509.MarshalPKCS8PrivateKey(priv)
+ if err != nil {
+ return "", "", err
+ }
+ privPem := pem.EncodeToMemory(
+ &pem.Block{
+ Type: "PRIVATE KEY",
+ Bytes: privEnc,
+ },
+ )
+ pubKey, err := ssh.NewPublicKey(pub)
+ if err != nil {
+ return "", "", err
+ }
+ return string(ssh.MarshalAuthorizedKey(pubKey)), string(privPem), nil
+}
+
+func readAdminKeys() ([]byte, []byte, error) {
+ pubKey, err := os.ReadFile(bootstrapFlags.adminPubKey)
+ if err != nil {
+ return nil, nil, err
+ }
+ privKey, err := os.ReadFile(bootstrapFlags.adminPrivKey)
+ if err != nil {
+ return nil, nil, err
+ }
+ return pubKey, privKey, nil
+}
diff --git a/core/installer/cmd/env.go b/core/installer/cmd/env.go
new file mode 100644
index 0000000..6f24d19
--- /dev/null
+++ b/core/installer/cmd/env.go
@@ -0,0 +1,51 @@
+package main
+
+import (
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/giolekva/pcloud/core/installer/soft"
+ "github.com/spf13/cobra"
+)
+
+var createEnvFlags struct {
+ name string
+ adminPrivKey string
+}
+
+func createEnvCmd() *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "create-env",
+ RunE: createEnvCmdRun,
+ }
+ cmd.Flags().StringVar(
+ &createEnvFlags.name,
+ "name",
+ "",
+ "",
+ )
+ cmd.Flags().StringVar(
+ &createEnvFlags.adminPrivKey,
+ "admin-priv-key",
+ "",
+ "",
+ )
+ return cmd
+}
+
+func createEnvCmdRun(cmd *cobra.Command, args []string) error {
+ adminPrivKey, err := os.ReadFile(createEnvFlags.adminPrivKey)
+ if err != nil {
+ return err
+ }
+ ss, err := soft.NewClient("192.168.0.208", 22, adminPrivKey, log.Default())
+ if err != nil {
+ return err
+ }
+ readme := fmt.Sprintf("# %s PCloud environment", createEnvFlags.name)
+ if err := ss.AddRepository(createEnvFlags.name, readme); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/core/installer/cmd/main.go b/core/installer/cmd/main.go
new file mode 100644
index 0000000..9b7a6ba
--- /dev/null
+++ b/core/installer/cmd/main.go
@@ -0,0 +1,34 @@
+package main
+
+import (
+ "log"
+
+ "github.com/spf13/cobra"
+)
+
+var rootCmd *cobra.Command
+
+var rootFlags struct {
+ kubeConfig string
+}
+
+func init() {
+ rootCmd = &cobra.Command{
+ Use: "pcloud",
+ }
+ rootCmd.PersistentFlags().StringVar(
+ &rootFlags.kubeConfig,
+ "kubeconfig",
+ "",
+ "",
+ )
+ rootCmd.AddCommand(bootstrapCmd())
+ rootCmd.AddCommand(createEnvCmd())
+ rootCmd.AddCommand(installCmd())
+}
+
+func main() {
+ if err := rootCmd.Execute(); err != nil {
+ log.Fatal(err)
+ }
+}