blob: f2e4369482d2cd3f743955c813dadaf62d5013b6 [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 Lekveishvili7efe22f2023-05-30 13:01:53 +040016 "sigs.k8s.io/yaml"
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040017)
18
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040019const appDirName = "apps"
20const configFileName = "config.yaml"
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040021const kustomizationFileName = "kustomization.yaml"
22
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040023type AppManager struct {
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040024 repo *git.Repository
25 signer ssh.Signer
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040026}
27
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040028// func NewAppManager(repo *git.Repository, fs billy.Filesystem, config Config, appRepo AppRepository) (*AppManager, error) {
29func NewAppManager(repo *git.Repository, signer ssh.Signer) (*AppManager, error) {
30 return &AppManager{
31 repo,
32 signer,
33 }, nil
34}
35
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +040036func (m *AppManager) Config() (Config, error) {
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040037 wt, err := m.repo.Worktree()
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040038 if err != nil {
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +040039 return Config{}, err
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040040 }
41 configF, err := wt.Filesystem.Open(configFileName)
42 if err != nil {
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +040043 return Config{}, err
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040044 }
45 defer configF.Close()
46 config, err := ReadConfig(configF)
47 if err != nil {
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +040048 return Config{}, err
49 }
50 return config, nil
51}
52
53func (m *AppManager) Install(app App, config map[string]any) error {
54 wt, err := m.repo.Worktree()
55 if err != nil {
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040056 return err
57 }
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +040058 globalConfig, err := m.Config()
59 if err != nil {
60 return err
61 }
62 all := map[string]any{
63 "Global": globalConfig.Values,
64 "Values": config,
65 }
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040066 appsRoot, err := wt.Filesystem.Chroot(appDirName)
67 if err != nil {
68 return err
69 }
70 rootKustF, err := appsRoot.Open(kustomizationFileName)
71 if err != nil {
72 return err
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040073 }
74 defer rootKustF.Close()
75 rootKust, err := ReadKustomization(rootKustF)
76 if err != nil {
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040077 return err
78 }
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040079 appRoot, err := appsRoot.Chroot(app.Name)
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040080 if err != nil {
81 return err
82 }
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040083 if err := util.RemoveAll(appRoot, app.Name); err != nil {
84 return err
85 }
86 if err := appRoot.MkdirAll(app.Name, fs.ModePerm); err != nil {
87 return nil
88 }
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040089 appKust := NewKustomization()
90 for _, t := range app.Templates {
91 out, err := appRoot.Create(t.Name())
92 if err != nil {
93 return err
94 }
95 defer out.Close()
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +040096 if err := t.Execute(out, all); err != nil {
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040097 return err
98 }
99 appKust.Resources = append(appKust.Resources, t.Name())
100 }
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400101 {
102 out, err := appRoot.Create(configFileName)
103 if err != nil {
104 return err
105 }
106 defer out.Close()
107 configBytes, err := yaml.Marshal(config)
108 if err != nil {
109 return err
110 }
111 if _, err := out.Write(configBytes); err != nil {
112 return err
113 }
114 }
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400115 appKustF, err := appRoot.Create(kustomizationFileName)
116 if err != nil {
117 return err
118 }
119 defer appKustF.Close()
120 if err := appKust.Write(appKustF); err != nil {
121 return err
122 }
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +0400123 if !slices.Contains(rootKust.Resources, app.Name) {
124 rootKust.Resources = append(rootKust.Resources, app.Name)
125 rootKustFW, err := appsRoot.Create(kustomizationFileName)
126 if err != nil {
127 return err
128 }
129 defer rootKustFW.Close()
130 if err := rootKust.Write(rootKustFW); err != nil {
131 return err
132 }
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400133 }
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +0400134 // Commit and push
135 if err := wt.AddGlob("*"); err != nil {
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400136 return err
137 }
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +0400138 if _, err := wt.Commit(fmt.Sprintf("install: %s", app.Name), &git.CommitOptions{
139 Author: &object.Signature{
140 Name: "pcloud-appmanager",
141 When: time.Now(),
142 },
143 }); err != nil {
144 return err
145 }
146 return m.repo.Push(&git.PushOptions{
147 RemoteName: "origin",
148 Auth: auth(m.signer),
149 })
150}
151
152func auth(signer ssh.Signer) *gitssh.PublicKeys {
153 return &gitssh.PublicKeys{
154 Signer: signer,
155 HostKeyCallbackHelper: gitssh.HostKeyCallbackHelper{
156 HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
157 // TODO(giolekva): verify server public key
158 // fmt.Printf("## %s || %s -- \n", serverPubKey, ssh.MarshalAuthorizedKey(key))
159 return nil
160 },
161 },
162 }
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400163}