blob: de56808f0ff3e6926aba9366743671a96700ba45 [file] [log] [blame]
giolekva8aa73e82022-07-09 11:34:39 +04001package installer
giolekva050609f2021-12-29 15:51:40 +04002
giolekva8aa73e82022-07-09 11:34:39 +04003import (
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +04004 "archive/tar"
Giorgi Lekveishvili3f697b12024-01-04 00:56:06 +04005 "bytes"
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +04006 "compress/gzip"
giolekva8aa73e82022-07-09 11:34:39 +04007 "embed"
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +04008 "encoding/json"
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +04009 "fmt"
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040010 htemplate "html/template"
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +040011 "io"
giolekva8aa73e82022-07-09 11:34:39 +040012 "log"
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +040013 "net/http"
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040014 "strings"
giolekva8aa73e82022-07-09 11:34:39 +040015 "text/template"
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +040016
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +040017 "cuelang.org/go/cue"
18 "cuelang.org/go/cue/cuecontext"
19 cueyaml "cuelang.org/go/encoding/yaml"
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +040020 "github.com/Masterminds/sprig/v3"
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +040021 "github.com/go-git/go-billy/v5"
22 "sigs.k8s.io/yaml"
giolekva8aa73e82022-07-09 11:34:39 +040023)
giolekva050609f2021-12-29 15:51:40 +040024
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040025//go:embed values-tmpl
26var valuesTmpls embed.FS
27
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +040028type Named interface {
29 Nam() string
30}
31
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +040032type appConfig struct {
33 Name string `json:"name"`
34 Version string `json:"version"`
35 Description string `json:"description"`
36 Namespaces []string `json:"namespaces"`
37 Icon htemplate.HTML `json:"icon"`
38}
39
giolekva050609f2021-12-29 15:51:40 +040040type App struct {
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +040041 Name string
42 Namespaces []string
Giorgi Lekveishvili3f697b12024-01-04 00:56:06 +040043 templates []*template.Template
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040044 schema Schema
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +040045 Readme *template.Template
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +040046 cfg *cue.Value
giolekva050609f2021-12-29 15:51:40 +040047}
48
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040049func (a App) Schema() Schema {
50 return a.schema
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040051}
52
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +040053type Rendered struct {
54 Readme string
55 Resources map[string][]byte
56}
57
58func (a App) Render(derived Derived) (Rendered, error) {
59 ret := Rendered{
60 Resources: make(map[string][]byte),
61 }
62 if a.cfg != nil {
63 var buf bytes.Buffer
64 if err := json.NewEncoder(&buf).Encode(derived); err != nil {
65 return Rendered{}, err
66 }
67 ctx := a.cfg.Context()
68 d := ctx.CompileBytes(buf.Bytes())
69 res := a.cfg.Unify(d).Eval()
70 if err := res.Err(); err != nil {
71 return Rendered{}, err
72 }
73 if err := res.Validate(); err != nil {
74 return Rendered{}, err
75 }
76 readme, err := res.LookupPath(cue.ParsePath("readme")).String()
77 if err != nil {
78 return Rendered{}, err
79 }
80 ret.Readme = readme
81 output := res.LookupPath(cue.ParsePath("output"))
82 i, err := output.Fields()
83 if err != nil {
84 return Rendered{}, err
85 }
86 for i.Next() {
87 name := i.Selector().String()
88 contents, err := cueyaml.Encode(i.Value())
89 if err != nil {
90 return Rendered{}, err
91 }
92 ret.Resources[name] = contents
93 }
94 return ret, nil
95 }
96 var readme bytes.Buffer
97 if err := a.Readme.Execute(&readme, derived); err != nil {
98 return Rendered{}, err
99 }
100 ret.Readme = readme.String()
Giorgi Lekveishvili3f697b12024-01-04 00:56:06 +0400101 for _, t := range a.templates {
102 var buf bytes.Buffer
103 if err := t.Execute(&buf, derived); err != nil {
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400104 return Rendered{}, err
Giorgi Lekveishvili3f697b12024-01-04 00:56:06 +0400105 }
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400106 ret.Resources[t.Name()] = buf.Bytes()
Giorgi Lekveishvili3f697b12024-01-04 00:56:06 +0400107 }
108 return ret, nil
109}
110
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400111type StoreApp struct {
112 App
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400113 Icon htemplate.HTML
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400114 ShortDescription string
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +0400115}
116
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400117func (a App) Nam() string {
118 return a.Name
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +0400119}
120
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400121func (a StoreApp) Nam() string {
122 return a.Name
123}
124
125type AppRepository[A Named] interface {
126 GetAll() ([]A, error)
127 Find(name string) (*A, error)
128}
129
130type InMemoryAppRepository[A Named] struct {
131 apps []A
132}
133
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400134func NewInMemoryAppRepository[A Named](apps []A) InMemoryAppRepository[A] {
135 return InMemoryAppRepository[A]{
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +0400136 apps,
137 }
138}
139
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400140func (r InMemoryAppRepository[A]) Find(name string) (*A, error) {
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +0400141 for _, a := range r.apps {
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400142 if a.Nam() == name {
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +0400143 return &a, nil
144 }
145 }
146 return nil, fmt.Errorf("Application not found: %s", name)
147}
giolekva8aa73e82022-07-09 11:34:39 +0400148
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400149func (r InMemoryAppRepository[A]) GetAll() ([]A, error) {
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400150 return r.apps, nil
151}
152
giolekva8aa73e82022-07-09 11:34:39 +0400153func CreateAllApps() []App {
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400154 tmpls, err := template.New("root").Funcs(template.FuncMap(sprig.FuncMap())).ParseFS(valuesTmpls, "values-tmpl/*")
giolekva8aa73e82022-07-09 11:34:39 +0400155 if err != nil {
156 log.Fatal(err)
157 }
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400158 ret := []App{
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400159 CreateAppIngressPrivate(valuesTmpls, tmpls),
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400160 CreateCertificateIssuerPublic(valuesTmpls, tmpls),
161 CreateCertificateIssuerPrivate(valuesTmpls, tmpls),
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400162 CreateAppCoreAuth(valuesTmpls, tmpls),
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400163 CreateAppHeadscale(valuesTmpls, tmpls),
Giorgi Lekveishvili39913692023-12-05 08:58:08 +0400164 CreateAppHeadscaleUser(valuesTmpls, tmpls),
Giorgi Lekveishvili4fc29432023-07-20 10:03:28 +0400165 CreateMetallbIPAddressPool(valuesTmpls, tmpls),
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400166 CreateEnvManager(valuesTmpls, tmpls),
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400167 CreateWelcome(valuesTmpls, tmpls),
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400168 CreateAppManager(valuesTmpls, tmpls),
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400169 CreateIngressPublic(valuesTmpls, tmpls),
170 CreateCertManager(valuesTmpls, tmpls),
171 CreateCertManagerWebhookGandi(valuesTmpls, tmpls),
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400172 CreateCSIDriverSMB(valuesTmpls, tmpls),
173 CreateResourceRendererController(valuesTmpls, tmpls),
174 CreateHeadscaleController(valuesTmpls, tmpls),
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400175 CreateDNSZoneManager(valuesTmpls, tmpls),
Giorgi Lekveishvili2df23db2023-12-14 07:55:22 +0400176 CreateFluxcdReconciler(valuesTmpls, tmpls),
giolekvaef76a3e2022-01-10 12:22:28 +0400177 }
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400178 for _, a := range CreateStoreApps() {
179 ret = append(ret, a.App)
180 }
181 return ret
182}
183
184func CreateStoreApps() []StoreApp {
185 tmpls, err := template.New("root").Funcs(template.FuncMap(sprig.FuncMap())).ParseFS(valuesTmpls, "values-tmpl/*")
186 if err != nil {
187 log.Fatal(err)
188 }
189 return []StoreApp{
190 CreateAppVaultwarden(valuesTmpls, tmpls),
191 CreateAppMatrix(valuesTmpls, tmpls),
192 CreateAppPihole(valuesTmpls, tmpls),
Giorgi Lekveishvili3fd5e4c2023-12-19 22:09:40 +0400193 CreateAppPenpot(valuesTmpls, tmpls),
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400194 CreateAppMaddy(valuesTmpls, tmpls),
195 CreateAppQBittorrent(valuesTmpls, tmpls),
196 CreateAppJellyfin(valuesTmpls, tmpls),
Giorgi Lekveishvili672af5d2023-07-12 11:57:51 +0400197 CreateAppSoftServe(valuesTmpls, tmpls),
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400198 CreateAppRpuppy(valuesTmpls, tmpls),
199 }
giolekvaef76a3e2022-01-10 12:22:28 +0400200}
201
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400202func readJSONSchemaFromFile(fs embed.FS, f string) (Schema, error) {
203 schema, err := fs.ReadFile(f)
204 if err != nil {
205 return nil, err
206 }
207 ret, err := NewJSONSchema(string(schema))
208 if err != nil {
209 return nil, err
210 }
211 return ret, nil
212}
213
Giorgi Lekveishvili4d2784d2023-06-01 14:27:32 +0400214// TODO(gio): service account needs permission to create/update secret
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400215func CreateAppIngressPrivate(fs embed.FS, tmpls *template.Template) App {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400216 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/private-network.jsonschema")
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400217 if err != nil {
218 panic(err)
219 }
giolekva050609f2021-12-29 15:51:40 +0400220 return App{
Giorgi Lekveishvili2dbce6c2023-12-05 15:16:27 +0400221 "private-network",
222 []string{"ingress-private"}, // TODO(gio): rename to private network
giolekva050609f2021-12-29 15:51:40 +0400223 []*template.Template{
giolekva050609f2021-12-29 15:51:40 +0400224 tmpls.Lookup("ingress-private.yaml"),
Giorgi Lekveishvili2dbce6c2023-12-05 15:16:27 +0400225 tmpls.Lookup("tailscale-proxy.yaml"),
giolekva050609f2021-12-29 15:51:40 +0400226 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400227 schema,
Giorgi Lekveishvili2dbce6c2023-12-05 15:16:27 +0400228 tmpls.Lookup("private-network.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400229 nil,
giolekva050609f2021-12-29 15:51:40 +0400230 }
231}
232
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400233func CreateCertificateIssuerPrivate(fs embed.FS, tmpls *template.Template) App {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400234 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/certificate-issuer-private.jsonschema")
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400235 if err != nil {
236 panic(err)
237 }
238 return App{
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400239 "certificate-issuer-private",
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400240 []string{},
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400241 []*template.Template{
242 tmpls.Lookup("certificate-issuer-private.yaml"),
243 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400244 schema,
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400245 tmpls.Lookup("certificate-issuer-private.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400246 nil,
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400247 }
248}
249
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400250func CreateCertificateIssuerPublic(fs embed.FS, tmpls *template.Template) App {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400251 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/certificate-issuer-public.jsonschema")
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400252 if err != nil {
253 panic(err)
254 }
255 return App{
256 "certificate-issuer-public",
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400257 []string{},
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400258 []*template.Template{
259 tmpls.Lookup("certificate-issuer-public.yaml"),
260 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400261 schema,
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400262 tmpls.Lookup("certificate-issuer-public.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400263 nil,
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400264 }
265}
266
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400267func CreateAppCoreAuth(fs embed.FS, tmpls *template.Template) App {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400268 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/core-auth.jsonschema")
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400269 if err != nil {
270 panic(err)
271 }
giolekva050609f2021-12-29 15:51:40 +0400272 return App{
273 "core-auth",
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400274 []string{"core-auth"},
giolekva050609f2021-12-29 15:51:40 +0400275 []*template.Template{
276 tmpls.Lookup("core-auth-storage.yaml"),
277 tmpls.Lookup("core-auth.yaml"),
278 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400279 schema,
Giorgi Lekveishvili3ca1f3f2023-05-30 14:33:02 +0400280 tmpls.Lookup("core-auth.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400281 nil,
giolekva050609f2021-12-29 15:51:40 +0400282 }
283}
284
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400285func CreateAppVaultwarden(fs embed.FS, tmpls *template.Template) StoreApp {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400286 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/vaultwarden.jsonschema")
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400287 if err != nil {
288 panic(err)
289 }
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400290 return StoreApp{
291 App: App{
292 "vaultwarden",
293 []string{"app-vaultwarden"},
294 []*template.Template{
295 tmpls.Lookup("vaultwarden.yaml"),
296 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400297 schema,
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400298 tmpls.Lookup("vaultwarden.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400299 nil,
giolekva050609f2021-12-29 15:51:40 +0400300 },
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400301 Icon: `<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 48 48"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M35.38 25.63V9.37H24v28.87a34.93 34.93 0 0 0 5.41-3.48q6-4.66 6-9.14Zm4.87-19.5v19.5A11.58 11.58 0 0 1 39.4 30a16.22 16.22 0 0 1-2.11 3.81a23.52 23.52 0 0 1-3 3.24a34.87 34.87 0 0 1-3.22 2.62c-1 .69-2 1.35-3.07 2s-1.82 1-2.27 1.26l-1.08.51a1.53 1.53 0 0 1-1.32 0l-1.08-.51c-.45-.22-1.21-.64-2.27-1.26s-2.09-1.27-3.07-2A34.87 34.87 0 0 1 13.7 37a23.52 23.52 0 0 1-3-3.24A16.22 16.22 0 0 1 8.6 30a11.58 11.58 0 0 1-.85-4.32V6.13A1.64 1.64 0 0 1 9.38 4.5h29.24a1.64 1.64 0 0 1 1.63 1.63Z"/></svg>`,
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400302 ShortDescription: "Open source implementation of Bitwarden password manager. Can be used with official client applications.",
giolekva050609f2021-12-29 15:51:40 +0400303 }
304}
305
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400306func CreateAppMatrix(fs embed.FS, tmpls *template.Template) StoreApp {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400307 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/matrix.jsonschema")
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400308 if err != nil {
309 panic(err)
310 }
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400311 return StoreApp{
312 App{
313 "matrix",
314 []string{"app-matrix"},
315 []*template.Template{
316 tmpls.Lookup("matrix-storage.yaml"),
317 tmpls.Lookup("matrix.yaml"),
318 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400319 schema,
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400320 nil,
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400321 nil,
giolekva050609f2021-12-29 15:51:40 +0400322 },
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400323 `<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 24 24"><path fill="currentColor" d="M.632.55v22.9H2.28V24H0V0h2.28v.55zm7.043 7.26v1.157h.033a3.312 3.312 0 0 1 1.117-1.024c.433-.245.936-.365 1.5-.365c.54 0 1.033.107 1.481.314c.448.208.785.582 1.02 1.108c.254-.374.6-.706 1.034-.992c.434-.287.95-.43 1.546-.43c.453 0 .872.056 1.26.167c.388.11.716.286.993.53c.276.245.489.559.646.951c.152.392.23.863.23 1.417v5.728h-2.349V11.52c0-.286-.01-.559-.032-.812a1.755 1.755 0 0 0-.18-.66a1.106 1.106 0 0 0-.438-.448c-.194-.11-.457-.166-.785-.166c-.332 0-.6.064-.803.189a1.38 1.38 0 0 0-.48.499a1.946 1.946 0 0 0-.231.696a5.56 5.56 0 0 0-.06.785v4.768h-2.35v-4.8c0-.254-.004-.503-.018-.752a2.074 2.074 0 0 0-.143-.688a1.052 1.052 0 0 0-.415-.503c-.194-.125-.476-.19-.854-.19c-.111 0-.259.024-.439.074c-.18.051-.36.143-.53.282a1.637 1.637 0 0 0-.439.595c-.12.259-.18.6-.18 1.02v4.966H5.46V7.81zm15.693 15.64V.55H21.72V0H24v24h-2.28v-.55z"/></svg>`,
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400324 "An open network for secure, decentralised communication",
giolekva050609f2021-12-29 15:51:40 +0400325 }
326}
327
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400328func CreateAppPihole(fs embed.FS, tmpls *template.Template) StoreApp {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400329 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/pihole.jsonschema")
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400330 if err != nil {
331 panic(err)
332 }
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400333 return StoreApp{
334 App{
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400335 "pihnole",
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400336 []string{"app-pihole"},
337 []*template.Template{
338 tmpls.Lookup("pihole.yaml"),
339 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400340 schema,
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400341 tmpls.Lookup("pihole.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400342 nil,
giolekva050609f2021-12-29 15:51:40 +0400343 },
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400344 // "simple-icons:pihole",
345 `<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 24 24"><path fill="currentColor" d="M4.344 0c.238 4.792 3.256 7.056 6.252 7.376c.165-1.692-4.319-5.6-4.319-5.6c-.008-.011.009-.025.019-.014c0 0 4.648 4.01 5.423 5.645c2.762-.15 5.196-1.947 5-4.912c0 0-4.12-.613-5 4.618C11.48 2.753 8.993 0 4.344 0zM12 7.682v.002a3.68 3.68 0 0 0-2.591 1.077L4.94 13.227a3.683 3.683 0 0 0-.86 1.356a3.31 3.31 0 0 0-.237 1.255A3.681 3.681 0 0 0 4.92 18.45l4.464 4.466a3.69 3.69 0 0 0 2.251 1.06l.002.001c.093.01.187.015.28.017l-.1-.008c.06.003.117.009.177.009l-.077-.001L12 24l-.004-.005a3.68 3.68 0 0 0 2.61-1.077l4.469-4.465a3.683 3.683 0 0 0 1.006-1.888l.012-.063a3.682 3.682 0 0 0 .057-.541l.003-.061c0-.017.003-.05.004-.06h-.002a3.683 3.683 0 0 0-1.077-2.607l-4.466-4.468a3.694 3.694 0 0 0-1.564-.927l-.07-.02a3.43 3.43 0 0 0-.946-.133L12 7.682zm3.165 3.357c.023 1.748-1.33 3.078-1.33 4.806c.164 2.227 1.733 3.207 3.266 3.146c-.035.003-.068.007-.104.009c-1.847.135-3.209-1.326-5.002-1.326c-2.23.164-3.21 1.736-3.147 3.27l-.008-.104c-.133-1.847 1.328-3.21 1.328-5.002c-.173-2.32-1.867-3.284-3.46-3.132c.1-.011.203-.021.31-.027c1.847-.133 3.209 1.328 5.002 1.328c2.082-.155 3.074-1.536 3.145-2.968zM4.344 0c.238 4.792 3.256 7.056 6.252 7.376c.165-1.692-4.319-5.6-4.319-5.6c-.008-.011.009-.025.019-.014c0 0 4.648 4.01 5.423 5.645c2.762-.15 5.196-1.947 5-4.912c0 0-4.12-.613-5 4.618C11.48 2.753 8.993 0 4.344 0zM12 7.682v.002a3.68 3.68 0 0 0-2.591 1.077L4.94 13.227a3.683 3.683 0 0 0-.86 1.356a3.31 3.31 0 0 0-.237 1.255A3.681 3.681 0 0 0 4.92 18.45l4.464 4.466a3.69 3.69 0 0 0 2.251 1.06l.002.001c.093.01.187.015.28.017l-.1-.008c.06.003.117.009.177.009l-.077-.001L12 24l-.004-.005a3.68 3.68 0 0 0 2.61-1.077l4.469-4.465a3.683 3.683 0 0 0 1.006-1.888l.012-.063a3.682 3.682 0 0 0 .057-.541l.003-.061c0-.017.003-.05.004-.06h-.002a3.683 3.683 0 0 0-1.077-2.607l-4.466-4.468a3.694 3.694 0 0 0-1.564-.927l-.07-.02a3.43 3.43 0 0 0-.946-.133L12 7.682zm3.165 3.357c.023 1.748-1.33 3.078-1.33 4.806c.164 2.227 1.733 3.207 3.266 3.146c-.035.003-.068.007-.104.009c-1.847.135-3.209-1.326-5.002-1.326c-2.23.164-3.21 1.736-3.147 3.27l-.008-.104c-.133-1.847 1.328-3.21 1.328-5.002c-.173-2.32-1.867-3.284-3.46-3.132c.1-.011.203-.021.31-.027c1.847-.133 3.209 1.328 5.002 1.328c2.082-.155 3.074-1.536 3.145-2.968z"/></svg>`,
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400346 "Pi-hole is a Linux network-level advertisement and Internet tracker blocking application which acts as a DNS sinkhole and optionally a DHCP server, intended for use on a private network.",
giolekva050609f2021-12-29 15:51:40 +0400347 }
348}
349
Giorgi Lekveishvili3fd5e4c2023-12-19 22:09:40 +0400350func CreateAppPenpot(fs embed.FS, tmpls *template.Template) StoreApp {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400351 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/penpot.jsonschema")
Giorgi Lekveishvili3fd5e4c2023-12-19 22:09:40 +0400352 if err != nil {
353 panic(err)
354 }
355 return StoreApp{
356 App{
357 "penpot",
358 []string{"app-penpot"},
359 []*template.Template{
360 tmpls.Lookup("penpot.yaml"),
361 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400362 schema,
Giorgi Lekveishvili3fd5e4c2023-12-19 22:09:40 +0400363 tmpls.Lookup("penpot.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400364 nil,
Giorgi Lekveishvili3fd5e4c2023-12-19 22:09:40 +0400365 },
366 // "simple-icons:pihole",
367 `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M7.654 0L5.13 3.554v2.01L2.934 6.608l-.02-.009v13.109l8.563 4.045L12 24l.523-.247l8.563-4.045V6.6l-.017.008l-2.196-1.045V3.555l-.077-.108L16.349.001l-2.524 3.554v.004L11.989.973l-1.823 2.566l-.065-.091zm.447 2.065l.976 1.374H6.232l.964-1.358zm8.694 0l.976 1.374h-2.845l.965-1.358zm-4.36.971l.976 1.375h-2.845l.965-1.359zM5.962 4.132h1.35v4.544l-1.35-.638Zm2.042 0h1.343v5.506l-1.343-.635zm6.652 0h1.35V9l-1.35.637zm2.042 0h1.343v3.905l-1.343.634zm-6.402.972h1.35v5.62l-1.35-.638zm2.042 0h1.343v4.993l-1.343.634zm6.534 1.493l1.188.486l-1.188.561zM5.13 6.6v1.047l-1.187-.561ZM3.96 8.251l7.517 3.55v10.795l-7.516-3.55zm16.08 0v10.794l-7.517 3.55V11.802z"/></svg>`,
368 "Penpot is the first Open Source design and prototyping platform meant for cross-domain teams. Non dependent on operating systems, Penpot is web based and works with open standards (SVG). Penpot invites designers all over the world to fall in love with open source while getting developers excited about the design process in return.",
369 }
370}
371
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400372func CreateAppMaddy(fs embed.FS, tmpls *template.Template) StoreApp {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400373 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/maddy.jsonschema")
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400374 if err != nil {
375 panic(err)
376 }
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400377 return StoreApp{
378 App{
379 "maddy",
380 []string{"app-maddy"},
381 []*template.Template{
382 tmpls.Lookup("maddy.yaml"),
383 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400384 schema,
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400385 nil,
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400386 nil,
giolekva050609f2021-12-29 15:51:40 +0400387 },
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400388 `<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 48 48"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M9.5 13c13.687 13.574 14.825 13.09 29 0"/><rect width="37" height="31" x="5.5" y="8.5" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" rx="2"/></svg>`,
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400389 "SMPT/IMAP server to communicate via email.",
giolekva050609f2021-12-29 15:51:40 +0400390 }
391}
giolekvaef76a3e2022-01-10 12:22:28 +0400392
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400393func CreateAppQBittorrent(fs embed.FS, tmpls *template.Template) StoreApp {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400394 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/qbittorrent.jsonschema")
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400395 if err != nil {
396 panic(err)
397 }
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400398 return StoreApp{
399 App{
400 "qbittorrent",
401 []string{"app-qbittorrent"},
402 []*template.Template{
403 tmpls.Lookup("qbittorrent.yaml"),
404 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400405 schema,
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400406 tmpls.Lookup("qbittorrent.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400407 nil,
giolekvaef76a3e2022-01-10 12:22:28 +0400408 },
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400409 `<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 48 48"><circle cx="24" cy="24" r="21.5" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M26.651 22.364a5.034 5.034 0 0 1 5.035-5.035h0a5.034 5.034 0 0 1 5.034 5.035v3.272a5.034 5.034 0 0 1-5.034 5.035h0a5.034 5.034 0 0 1-5.035-5.035m0 5.035V10.533m-5.302 15.103a5.034 5.034 0 0 1-5.035 5.035h0a5.034 5.034 0 0 1-5.034-5.035v-3.272a5.034 5.034 0 0 1 5.034-5.035h0a5.034 5.034 0 0 1 5.035 5.035m0-5.035v20.138"/></svg>`,
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400410 "qBittorrent is a cross-platform free and open-source BitTorrent client written in native C++. It relies on Boost, Qt 6 toolkit and the libtorrent-rasterbar library, with an optional search engine written in Python.",
giolekvaef76a3e2022-01-10 12:22:28 +0400411 }
412}
413
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400414func CreateAppJellyfin(fs embed.FS, tmpls *template.Template) StoreApp {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400415 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/jellyfin.jsonschema")
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400416 if err != nil {
417 panic(err)
418 }
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400419 return StoreApp{
420 App{
421 "jellyfin",
422 []string{"app-jellyfin"},
423 []*template.Template{
424 tmpls.Lookup("jellyfin.yaml"),
425 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400426 schema,
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400427 nil,
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400428 nil,
giolekvaef76a3e2022-01-10 12:22:28 +0400429 },
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400430 `<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 48 48"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M24 20c-1.62 0-6.85 9.48-6.06 11.08s11.33 1.59 12.12 0S25.63 20 24 20Z"/><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M24 5.5c-4.89 0-20.66 28.58-18.25 33.4s34.13 4.77 36.51 0S28.9 5.5 24 5.5Zm12 29.21c-1.56 3.13-22.35 3.17-23.93 0S20.8 12.83 24 12.83s13.52 18.76 12 21.88Z"/></svg>`,
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400431 "Jellyfin is a free and open-source media server and suite of multimedia applications designed to organize, manage, and share digital media files to networked devices.",
giolekvaef76a3e2022-01-10 12:22:28 +0400432 }
433}
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400434
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400435func CreateAppRpuppy(fs embed.FS, tmpls *template.Template) StoreApp {
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400436 contents, err := fs.ReadFile("values-tmpl/rpuppy.cue")
437 if err != nil {
438 panic(err)
439 }
440 ctx := cuecontext.New()
441 cfg := ctx.CompileBytes(contents)
442 if cfg.Err() != nil {
443 panic(cfg.Err())
444 }
445 if err := cfg.Validate(); err != nil {
446 panic(err)
447 }
448 schema, err := NewCueSchema(cfg.LookupPath(cue.ParsePath("input")))
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400449 if err != nil {
450 panic(err)
451 }
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400452 return StoreApp{
453 App{
454 "rpuppy",
455 []string{"app-rpuppy"},
456 []*template.Template{
457 tmpls.Lookup("rpuppy.yaml"),
458 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400459 schema,
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400460 tmpls.Lookup("rpuppy.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400461 &cfg,
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400462 },
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400463 `<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 256 256"><path fill="currentColor" d="M100 140a8 8 0 1 1-8-8a8 8 0 0 1 8 8Zm64 8a8 8 0 1 0-8-8a8 8 0 0 0 8 8Zm64.94-9.11a12.12 12.12 0 0 1-5 1.11a11.83 11.83 0 0 1-9.35-4.62l-2.59-3.29V184a36 36 0 0 1-36 36H80a36 36 0 0 1-36-36v-51.91l-2.53 3.27A11.88 11.88 0 0 1 32.1 140a12.08 12.08 0 0 1-5-1.11a11.82 11.82 0 0 1-6.84-13.14l16.42-88a12 12 0 0 1 14.7-9.43h.16L104.58 44h46.84l53.08-15.6h.16a12 12 0 0 1 14.7 9.43l16.42 88a11.81 11.81 0 0 1-6.84 13.06ZM97.25 50.18L49.34 36.1a4.18 4.18 0 0 0-.92-.1a4 4 0 0 0-3.92 3.26l-16.42 88a4 4 0 0 0 7.08 3.22ZM204 121.75L150 52h-44l-54 69.75V184a28 28 0 0 0 28 28h44v-18.34l-14.83-14.83a4 4 0 0 1 5.66-5.66L128 186.34l13.17-13.17a4 4 0 0 1 5.66 5.66L132 193.66V212h44a28 28 0 0 0 28-28Zm23.92 5.48l-16.42-88a4 4 0 0 0-4.84-3.16l-47.91 14.11l62.11 80.28a4 4 0 0 0 7.06-3.23Z"/></svg>`,
Giorgi Lekveishvili27b2b572023-06-30 10:44:45 +0400464 "Delights users with randomly generate puppy pictures. Can be configured to be reachable only from private network or publicly.",
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400465 }
466}
467
Giorgi Lekveishvili672af5d2023-07-12 11:57:51 +0400468func CreateAppSoftServe(fs embed.FS, tmpls *template.Template) StoreApp {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400469 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/soft-serve.jsonschema")
Giorgi Lekveishvili672af5d2023-07-12 11:57:51 +0400470 if err != nil {
471 panic(err)
472 }
473 return StoreApp{
474 App{
475 "soft-serve",
476 []string{"app-soft-serve"},
477 []*template.Template{
478 tmpls.Lookup("soft-serve.yaml"),
479 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400480 schema,
Giorgi Lekveishvili672af5d2023-07-12 11:57:51 +0400481 tmpls.Lookup("soft-serve.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400482 nil,
Giorgi Lekveishvili672af5d2023-07-12 11:57:51 +0400483 },
484 `<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 48 48"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="4"><path stroke-linejoin="round" d="M15.34 22.5L21 37l3 6l3-6l5.66-14.5"/><path d="M19 32h10"/><path stroke-linejoin="round" d="M24 3c-6 0-8 6-8 6s-6 2-6 7s5 7 5 7s3.5-2 9-2s9 2 9 2s5-2 5-7s-6-7-6-7s-2-6-8-6Z"/></g></svg>`,
485 "A tasty, self-hostable Git server for the command line. 🍦",
486 }
487}
488
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400489func CreateAppHeadscale(fs embed.FS, tmpls *template.Template) App {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400490 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/headscale.jsonschema")
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400491 if err != nil {
492 panic(err)
493 }
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400494 return App{
495 "headscale",
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400496 []string{"app-headscale"},
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400497 []*template.Template{
498 tmpls.Lookup("headscale.yaml"),
499 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400500 schema,
Giorgi Lekveishvili3a907052023-05-30 13:33:32 +0400501 tmpls.Lookup("headscale.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400502 nil,
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400503 }
504}
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400505
Giorgi Lekveishvili39913692023-12-05 08:58:08 +0400506func CreateAppHeadscaleUser(fs embed.FS, tmpls *template.Template) App {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400507 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/headscale-user.jsonschema")
Giorgi Lekveishvili39913692023-12-05 08:58:08 +0400508 if err != nil {
509 panic(err)
510 }
511 return App{
512 "headscale-user",
513 []string{"app-headscale"},
514 []*template.Template{
515 tmpls.Lookup("headscale-user.yaml"),
516 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400517 schema,
Giorgi Lekveishvili39913692023-12-05 08:58:08 +0400518 tmpls.Lookup("headscale-user.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400519 nil,
Giorgi Lekveishvili39913692023-12-05 08:58:08 +0400520 }
521}
522
Giorgi Lekveishvili4fc29432023-07-20 10:03:28 +0400523func CreateMetallbIPAddressPool(fs embed.FS, tmpls *template.Template) App {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400524 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/metallb-ipaddresspool.jsonschema")
Giorgi Lekveishvili4fc29432023-07-20 10:03:28 +0400525 if err != nil {
526 panic(err)
527 }
528 return App{
529 "metallb-ipaddresspool",
530 []string{"metallb-ipaddresspool"},
531 []*template.Template{
532 tmpls.Lookup("metallb-ipaddresspool.yaml"),
533 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400534 schema,
Giorgi Lekveishvili4fc29432023-07-20 10:03:28 +0400535 tmpls.Lookup("metallb-ipaddresspool.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400536 nil,
Giorgi Lekveishvili4fc29432023-07-20 10:03:28 +0400537 }
538}
539
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400540func CreateEnvManager(fs embed.FS, tmpls *template.Template) App {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400541 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/env-manager.jsonschema")
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400542 if err != nil {
543 panic(err)
544 }
545 return App{
546 "env-manager",
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400547 []string{"env-manager"},
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400548 []*template.Template{
549 tmpls.Lookup("env-manager.yaml"),
550 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400551 schema,
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400552 tmpls.Lookup("env-manager.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400553 nil,
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400554 }
555}
556
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400557func CreateWelcome(fs embed.FS, tmpls *template.Template) App {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400558 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/welcome.jsonschema")
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400559 if err != nil {
560 panic(err)
561 }
562 return App{
563 "welcome",
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400564 []string{"app-welcome"},
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400565 []*template.Template{
566 tmpls.Lookup("welcome.yaml"),
567 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400568 schema,
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400569 tmpls.Lookup("welcome.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400570 nil,
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400571 }
572}
573
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400574func CreateAppManager(fs embed.FS, tmpls *template.Template) App {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400575 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/appmanager.jsonschema")
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400576 if err != nil {
577 panic(err)
578 }
579 return App{
580 "app-manager",
581 []string{"core-appmanager"},
582 []*template.Template{
583 tmpls.Lookup("appmanager.yaml"),
584 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400585 schema,
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400586 tmpls.Lookup("appmanager.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400587 nil,
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400588 }
589}
590
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400591func CreateIngressPublic(fs embed.FS, tmpls *template.Template) App {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400592 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/ingress-public.jsonschema")
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400593 if err != nil {
594 panic(err)
595 }
596 return App{
597 "ingress-public",
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400598 []string{"ingress-public"},
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400599 []*template.Template{
600 tmpls.Lookup("ingress-public.yaml"),
601 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400602 schema,
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400603 tmpls.Lookup("ingress-public.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400604 nil,
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400605 }
606}
607
608func CreateCertManager(fs embed.FS, tmpls *template.Template) App {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400609 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/cert-manager.jsonschema")
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400610 if err != nil {
611 panic(err)
612 }
613 return App{
614 "cert-manager",
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400615 []string{"cert-manager"},
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400616 []*template.Template{
617 tmpls.Lookup("cert-manager.yaml"),
618 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400619 schema,
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400620 tmpls.Lookup("cert-manager.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400621 nil,
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400622 }
623}
624
625func CreateCertManagerWebhookGandi(fs embed.FS, tmpls *template.Template) App {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400626 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/cert-manager-webhook-pcloud.jsonschema")
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400627 if err != nil {
628 panic(err)
629 }
630 return App{
Giorgi Lekveishvili5c2c0b92023-12-07 17:35:40 +0400631 "cert-manager-webhook-pcloud",
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400632 []string{},
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400633 []*template.Template{
Giorgi Lekveishvili5c2c0b92023-12-07 17:35:40 +0400634 tmpls.Lookup("cert-manager-webhook-pcloud.yaml"),
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400635 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400636 schema,
Giorgi Lekveishvili5c2c0b92023-12-07 17:35:40 +0400637 tmpls.Lookup("cert-manager-webhook-pcloud.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400638 nil,
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400639 }
640}
641
642func CreateCSIDriverSMB(fs embed.FS, tmpls *template.Template) App {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400643 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/csi-driver-smb.jsonschema")
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400644 if err != nil {
645 panic(err)
646 }
647 return App{
648 "csi-driver-smb",
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400649 []string{"csi-driver-smb"},
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400650 []*template.Template{
651 tmpls.Lookup("csi-driver-smb.yaml"),
652 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400653 schema,
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400654 tmpls.Lookup("csi-driver-smb.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400655 nil,
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400656 }
657}
658
659func CreateResourceRendererController(fs embed.FS, tmpls *template.Template) App {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400660 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/resource-renderer-controller.jsonschema")
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400661 if err != nil {
662 panic(err)
663 }
664 return App{
665 "resource-renderer-controller",
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400666 []string{"rr-controller"},
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400667 []*template.Template{
668 tmpls.Lookup("resource-renderer-controller.yaml"),
669 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400670 schema,
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400671 tmpls.Lookup("resource-renderer-controller.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400672 nil,
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400673 }
674}
675
676func CreateHeadscaleController(fs embed.FS, tmpls *template.Template) App {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400677 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/headscale-controller.jsonschema")
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400678 if err != nil {
679 panic(err)
680 }
681 return App{
682 "headscale-controller",
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400683 []string{"headscale-controller"},
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400684 []*template.Template{
685 tmpls.Lookup("headscale-controller.yaml"),
686 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400687 schema,
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400688 tmpls.Lookup("headscale-controller.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400689 nil,
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400690 }
691}
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400692
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400693func CreateDNSZoneManager(fs embed.FS, tmpls *template.Template) App {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400694 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/dns-zone-controller.jsonschema")
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400695 if err != nil {
696 panic(err)
697 }
698 return App{
699 "dns-zone-manager",
700 []string{"dns-zone-manager"},
701 []*template.Template{
702 tmpls.Lookup("dns-zone-storage.yaml"),
703 tmpls.Lookup("coredns.yaml"),
704 tmpls.Lookup("dns-zone-controller.yaml"),
705 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400706 schema,
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400707 tmpls.Lookup("dns-zone-controller.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400708 nil,
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400709 }
710}
711
Giorgi Lekveishvili2df23db2023-12-14 07:55:22 +0400712func CreateFluxcdReconciler(fs embed.FS, tmpls *template.Template) App {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400713 schema, err := readJSONSchemaFromFile(fs, "values-tmpl/fluxcd-reconciler.jsonschema")
Giorgi Lekveishvili2df23db2023-12-14 07:55:22 +0400714 if err != nil {
715 panic(err)
716 }
717 return App{
718 "fluxcd-reconciler",
719 []string{"fluxcd-reconciler"},
720 []*template.Template{
721 tmpls.Lookup("fluxcd-reconciler.yaml"),
722 },
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400723 schema,
Giorgi Lekveishvili2df23db2023-12-14 07:55:22 +0400724 tmpls.Lookup("fluxcd-reconciler.md"),
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400725 nil,
Giorgi Lekveishvili2df23db2023-12-14 07:55:22 +0400726 }
727}
728
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400729type httpAppRepository struct {
730 apps []StoreApp
731}
732
733type appVersion struct {
734 Version string `json:"version"`
735 Urls []string `json:"urls"`
736}
737
738type allAppsResp struct {
739 ApiVersion string `json:"apiVersion"`
740 Entries map[string][]appVersion `json:"entries"`
741}
742
743func FetchAppsFromHTTPRepository(addr string, fs billy.Filesystem) error {
744 resp, err := http.Get(addr)
745 if err != nil {
746 return err
747 }
748 b, err := io.ReadAll(resp.Body)
749 if err != nil {
750 return err
751 }
752 var apps allAppsResp
753 if err := yaml.Unmarshal(b, &apps); err != nil {
754 return err
755 }
756 for name, conf := range apps.Entries {
757 for _, version := range conf {
758 resp, err := http.Get(version.Urls[0])
759 if err != nil {
760 return err
761 }
762 nameVersion := fmt.Sprintf("%s-%s", name, version.Version)
763 if err := fs.MkdirAll(nameVersion, 0700); err != nil {
764 return err
765 }
766 sub, err := fs.Chroot(nameVersion)
767 if err != nil {
768 return err
769 }
770 if err := extractApp(resp.Body, sub); err != nil {
771 return err
772 }
773 }
774 }
775 return nil
776}
777
778func extractApp(archive io.Reader, fs billy.Filesystem) error {
779 uncompressed, err := gzip.NewReader(archive)
780 if err != nil {
781 return err
782 }
783 tarReader := tar.NewReader(uncompressed)
784 for true {
785 header, err := tarReader.Next()
786 if err == io.EOF {
787 break
788 }
789 if err != nil {
790 return err
791 }
792 switch header.Typeflag {
793 case tar.TypeDir:
794 if err := fs.MkdirAll(header.Name, 0755); err != nil {
795 return err
796 }
797 case tar.TypeReg:
798 out, err := fs.Create(header.Name)
799 if err != nil {
800 return err
801 }
802 defer out.Close()
803 if _, err := io.Copy(out, tarReader); err != nil {
804 return err
805 }
806 default:
807 return fmt.Errorf("Uknown type: %s", header.Name)
808 }
809 }
810 return nil
811}
812
813type fsAppRepository struct {
814 InMemoryAppRepository[StoreApp]
815 fs billy.Filesystem
816}
817
818func NewFSAppRepository(fs billy.Filesystem) (AppRepository[StoreApp], error) {
819 all, err := fs.ReadDir(".")
820 if err != nil {
821 return nil, err
822 }
823 apps := make([]StoreApp, 0)
824 for _, e := range all {
825 if !e.IsDir() {
826 continue
827 }
828 appFS, err := fs.Chroot(e.Name())
829 if err != nil {
830 return nil, err
831 }
832 app, err := loadApp(appFS)
833 if err != nil {
834 log.Printf("Ignoring directory %s: %s", e.Name(), err)
835 continue
836 }
837 apps = append(apps, app)
838 }
839 return &fsAppRepository{
840 NewInMemoryAppRepository[StoreApp](apps),
841 fs,
842 }, nil
843}
844
845func loadApp(fs billy.Filesystem) (StoreApp, error) {
846 cfg, err := fs.Open("Chart.yaml")
847 if err != nil {
848 return StoreApp{}, err
849 }
850 defer cfg.Close()
851 b, err := io.ReadAll(cfg)
852 if err != nil {
853 return StoreApp{}, err
854 }
855 var appCfg appConfig
856 if err := yaml.Unmarshal(b, &appCfg); err != nil {
857 return StoreApp{}, err
858 }
859 rb, err := fs.Open("README.md")
860 if err != nil {
861 return StoreApp{}, err
862 }
863 defer rb.Close()
864 readme, err := io.ReadAll(rb)
865 if err != nil {
866 return StoreApp{}, err
867 }
868 readmeTmpl, err := template.New("README.md").Parse(string(readme))
869 if err != nil {
870 return StoreApp{}, err
871 }
872 sb, err := fs.Open("schema.json")
873 if err != nil {
874 return StoreApp{}, err
875 }
876 defer sb.Close()
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400877 scm, err := io.ReadAll(sb)
878 if err != nil {
879 return StoreApp{}, err
880 }
881 schema, err := NewJSONSchema(string(scm))
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400882 if err != nil {
883 return StoreApp{}, err
884 }
885 tFiles, err := fs.ReadDir("templates")
886 if err != nil {
887 return StoreApp{}, err
888 }
889 tmpls := make([]*template.Template, 0)
890 for _, t := range tFiles {
891 if !strings.HasSuffix(t.Name(), ".yaml") {
892 continue
893 }
894 inp, err := fs.Open(fs.Join("templates", t.Name()))
895 if err != nil {
896 return StoreApp{}, err
897 }
898 b, err := io.ReadAll(inp)
899 if err != nil {
900 return StoreApp{}, err
901 }
902 tmpl, err := template.New(t.Name()).Parse(string(b))
903 if err != nil {
904 return StoreApp{}, err
905 }
906 tmpls = append(tmpls, tmpl)
907 }
908 return StoreApp{
909 App: App{
910 Name: appCfg.Name,
911 Readme: readmeTmpl,
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +0400912 schema: schema,
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400913 Namespaces: appCfg.Namespaces,
Giorgi Lekveishvili3f697b12024-01-04 00:56:06 +0400914 templates: tmpls,
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400915 },
916 ShortDescription: appCfg.Description,
917 Icon: appCfg.Icon,
918 }, nil
919}