blob: 0678114c34255681da778c73d32bf9d1eab1466d [file] [log] [blame]
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +04001package installer
2
3import (
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +04004 "fmt"
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +04005 "io/fs"
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +04006 "net"
7 "time"
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +04008
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +04009 "golang.org/x/crypto/ssh"
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040010 "golang.org/x/exp/slices"
11
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040012 "github.com/go-git/go-billy/v5/util"
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040013 "github.com/go-git/go-git/v5"
14 "github.com/go-git/go-git/v5/plumbing/object"
15 gitssh "github.com/go-git/go-git/v5/plumbing/transport/ssh"
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040016)
17
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040018const appDirName = "apps"
19const configFileName = "config.yaml"
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040020const kustomizationFileName = "kustomization.yaml"
21
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040022type AppManager struct {
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040023 repo *git.Repository
24 signer ssh.Signer
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040025}
26
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040027// func NewAppManager(repo *git.Repository, fs billy.Filesystem, config Config, appRepo AppRepository) (*AppManager, error) {
28func NewAppManager(repo *git.Repository, signer ssh.Signer) (*AppManager, error) {
29 return &AppManager{
30 repo,
31 signer,
32 }, nil
33}
34
35func (m *AppManager) Install(app App) error {
36 wt, err := m.repo.Worktree()
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040037 if err != nil {
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040038 return err
39 }
40 configF, err := wt.Filesystem.Open(configFileName)
41 if err != nil {
42 return err
43 }
44 defer configF.Close()
45 config, err := ReadConfig(configF)
46 if err != nil {
47 return err
48 }
49 appsRoot, err := wt.Filesystem.Chroot(appDirName)
50 if err != nil {
51 return err
52 }
53 rootKustF, err := appsRoot.Open(kustomizationFileName)
54 if err != nil {
55 return err
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040056 }
57 defer rootKustF.Close()
58 rootKust, err := ReadKustomization(rootKustF)
59 if err != nil {
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040060 return err
61 }
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040062 appRoot, err := appsRoot.Chroot(app.Name)
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040063 if err != nil {
64 return err
65 }
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040066 if err := util.RemoveAll(appRoot, app.Name); err != nil {
67 return err
68 }
69 if err := appRoot.MkdirAll(app.Name, fs.ModePerm); err != nil {
70 return nil
71 }
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040072 appKust := NewKustomization()
73 for _, t := range app.Templates {
74 out, err := appRoot.Create(t.Name())
75 if err != nil {
76 return err
77 }
78 defer out.Close()
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040079 if err := t.Execute(out, config); err != nil {
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040080 return err
81 }
82 appKust.Resources = append(appKust.Resources, t.Name())
83 }
84 appKustF, err := appRoot.Create(kustomizationFileName)
85 if err != nil {
86 return err
87 }
88 defer appKustF.Close()
89 if err := appKust.Write(appKustF); err != nil {
90 return err
91 }
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040092 if !slices.Contains(rootKust.Resources, app.Name) {
93 rootKust.Resources = append(rootKust.Resources, app.Name)
94 rootKustFW, err := appsRoot.Create(kustomizationFileName)
95 if err != nil {
96 return err
97 }
98 defer rootKustFW.Close()
99 if err := rootKust.Write(rootKustFW); err != nil {
100 return err
101 }
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400102 }
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +0400103 // Commit and push
104 if err := wt.AddGlob("*"); err != nil {
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400105 return err
106 }
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +0400107 if _, err := wt.Commit(fmt.Sprintf("install: %s", app.Name), &git.CommitOptions{
108 Author: &object.Signature{
109 Name: "pcloud-appmanager",
110 When: time.Now(),
111 },
112 }); err != nil {
113 return err
114 }
115 return m.repo.Push(&git.PushOptions{
116 RemoteName: "origin",
117 Auth: auth(m.signer),
118 })
119}
120
121func auth(signer ssh.Signer) *gitssh.PublicKeys {
122 return &gitssh.PublicKeys{
123 Signer: signer,
124 HostKeyCallbackHelper: gitssh.HostKeyCallbackHelper{
125 HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
126 // TODO(giolekva): verify server public key
127 // fmt.Printf("## %s || %s -- \n", serverPubKey, ssh.MarshalAuthorizedKey(key))
128 return nil
129 },
130 },
131 }
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400132}