DodoApp: Implement commit status page
Render used volume, postgresql and ingress resource details.
Change-Id: I87f34fd19d0d0d31ec495d2798c9f5ce99c0fd43
diff --git a/core/installer/app_configs/app_base.cue b/core/installer/app_configs/app_base.cue
index 4954bef..5035fc7 100644
--- a/core/installer/app_configs/app_base.cue
+++ b/core/installer/app_configs/app_base.cue
@@ -49,6 +49,13 @@
volumes: {}
volumes: {
+ for _, p in _postgresql {
+ for k, v in p.out.volumes {
+ "\(k)": v
+ }
+ }
+}
+volumes: {
for key, value in volumes {
"\(key)": #volume & value & {
name: key
@@ -163,7 +170,7 @@
size: string | *"1Gi"
_size: size
- _volumeClaimName: "postgresql"
+ _volumeClaimName: "\(name)-postgresql"
out: {
images: {
@@ -175,12 +182,6 @@
}
}
charts: {
- volume: #Chart & {
- kind: "GitRepository"
- address: "https://code.v1.dodo.cloud/helm-charts"
- branch: "main"
- path: "charts/volumes"
- }
postgres: #Chart & {
kind: "GitRepository"
address: "https://code.v1.dodo.cloud/helm-charts"
@@ -188,6 +189,9 @@
path: "charts/postgresql"
}
}
+ volumes: {
+ "\(_volumeClaimName)": size: _size
+ }
charts: {
for key, value in charts {
"\(key)": #Chart & value & {
@@ -196,15 +200,14 @@
}
}
helm: {
- "volume-name": {
- chart: charts.volume
- values: {
- name: _volumeClaimName
- size: _size
- }
- }
postgres: {
chart: charts.postgres
+ annotations: {
+ "dodo.cloud/resource-type": "postgresql"
+ "dodo.cloud/resource.postgresql.name": name
+ "dodo.cloud/resource.postgresql.version": version
+ "dodo.cloud/resource.postgresql.volume": _volumeClaimName
+ }
values: {
fullnameOverride: "postgres"
image: {
@@ -270,6 +273,7 @@
name: string
dependsOn: [...#ResourceReference] | *[]
info: string | *""
+ annotations: {...} | *{}
...
}
@@ -284,6 +288,11 @@
"\(key)-volume": #Helm & {
chart: charts.volume
info: "Creating disk for \(key)"
+ annotations: {
+ "dodo.cloud/resource-type": "volume"
+ "dodo.cloud/resource.volume.name": value.name
+ "dodo.cloud/resource.volume.size": value.size
+ }
values: value
}
}
@@ -309,13 +318,14 @@
_values: _
_dependencies: [...#ResourceReference] | *[]
_info: string | *""
+ _annotations: {...} | *{}
apiVersion: "helm.toolkit.fluxcd.io/v2beta1"
kind: "HelmRelease"
metadata: {
name: _name
namespace: release.namespace
- annotations: {
+ annotations: _annotations & {
"dodo.cloud/installer-info": _info
}
}
@@ -335,6 +345,7 @@
_values: r.values
_dependencies: r.dependsOn
_info: r.info
+ _annotations: r.annotations
}
}
}
diff --git a/core/installer/app_configs/app_global_env.cue b/core/installer/app_configs/app_global_env.cue
index c53df4a..1681a1b 100644
--- a/core/installer/app_configs/app_global_env.cue
+++ b/core/installer/app_configs/app_global_env.cue
@@ -101,6 +101,10 @@
chart: charts.ingress
_service: service
info: "Generating TLS certificate for https://\(_domain)"
+ annotations: {
+ "dodo.cloud/resource-type": "ingress"
+ "dodo.cloud/resource.ingress.host": "https://\(_domain)"
+ }
values: {
domain: _domain
appRoot: _appRoot
diff --git a/core/installer/app_manager.go b/core/installer/app_manager.go
index a7c23f0..c27b641 100644
--- a/core/installer/app_manager.go
+++ b/core/installer/app_manager.go
@@ -285,9 +285,10 @@
}
type Resource struct {
- Name string `json:"name"`
- Namespace string `json:"namespace"`
- Info string `json:"info"`
+ Name string `json:"name"`
+ Namespace string `json:"namespace"`
+ Info string `json:"info"`
+ Annotations map[string]string `json:"annotations"`
}
type ReleaseResources struct {
@@ -326,7 +327,7 @@
if o.NoLock {
dopts = append(dopts, soft.WithNoLock())
}
- return repo.Do(func(r soft.RepoFS) (string, error) {
+ _, err := repo.Do(func(r soft.RepoFS) (string, error) {
if err := r.RemoveDir(appDir); err != nil {
return "", err
}
@@ -386,6 +387,7 @@
}
return fmt.Sprintf("install: %s", name), nil
}, dopts...)
+ return err
}
// TODO(gio): commit instanceId -> appDir mapping as well
@@ -529,11 +531,13 @@
}
if h.Kind == "HelmRelease" {
res := Resource{
- Name: h.Metadata.Name,
- Namespace: h.Metadata.Namespace,
- Info: fmt.Sprintf("%s/%s", h.Metadata.Namespace, h.Metadata.Name),
+ Name: h.Metadata.Name,
+ Namespace: h.Metadata.Namespace,
+ Info: fmt.Sprintf("%s/%s", h.Metadata.Namespace, h.Metadata.Name),
+ Annotations: nil,
}
if h.Metadata.Annotations != nil {
+ res.Annotations = h.Metadata.Annotations
info, ok := h.Metadata.Annotations["dodo.cloud/installer-info"]
if ok && len(info) != 0 {
res.Info = info
@@ -599,7 +603,7 @@
return err
}
var portForward []PortForward
- if err := m.repoIO.Do(func(r soft.RepoFS) (string, error) {
+ if _, err := m.repoIO.Do(func(r soft.RepoFS) (string, error) {
instanceDir := filepath.Join(m.appDirRoot, instanceId)
renderedCfg, err := readRendered(m.repoIO, filepath.Join(instanceDir, "rendered.json"))
if err != nil {
diff --git a/core/installer/bootstrapper.go b/core/installer/bootstrapper.go
index 4b72556..8a83eea 100644
--- a/core/installer/bootstrapper.go
+++ b/core/installer/bootstrapper.go
@@ -343,7 +343,7 @@
if err != nil {
return err
}
- if err := repoIO.Do(func(r soft.RepoFS) (string, error) {
+ if _, err := repoIO.Do(func(r soft.RepoFS) (string, error) {
w, err := r.Writer("README.md")
if err != nil {
return "", err
@@ -438,7 +438,7 @@
}
func configureMainRepo(repo soft.RepoIO, bootstrap BootstrapConfig) error {
- return repo.Do(func(r soft.RepoFS) (string, error) {
+ _, err := repo.Do(func(r soft.RepoFS) (string, error) {
if err := soft.WriteYaml(r, "bootstrap-config.yaml", bootstrap); err != nil {
return "", err
}
@@ -495,6 +495,7 @@
}
return "initialize pcloud directory structure", nil
})
+ return err
}
func (b Bootstrapper) installEnvManager(mgr *InfraAppManager, ss soft.Client, env BootstrapConfig) error {
diff --git a/core/installer/soft/repoio.go b/core/installer/soft/repoio.go
index f4c05bb..191b291 100644
--- a/core/installer/soft/repoio.go
+++ b/core/installer/soft/repoio.go
@@ -98,8 +98,8 @@
RepoFS
FullAddress() string
Pull() error
- CommitAndPush(message string, opts ...PushOption) error
- Do(op DoFn, opts ...DoOption) error
+ CommitAndPush(message string, opts ...PushOption) (string, error)
+ Do(op DoFn, opts ...DoOption) (string, error)
}
type repoFS struct {
@@ -190,32 +190,33 @@
return nil
}
-func (r *repoIO) CommitAndPush(message string, opts ...PushOption) error {
+func (r *repoIO) CommitAndPush(message string, opts ...PushOption) (string, error) {
var o pushOptions
for _, i := range opts {
i(&o)
}
wt, err := r.repo.Worktree()
if err != nil {
- return err
+ return "", err
}
if err := wt.AddGlob("*"); err != nil {
- return err
+ return "", err
}
st, err := wt.Status()
if err != nil {
- return err
+ return "", err
}
if len(st) == 0 {
- return nil // TODO(gio): maybe return ErrorNothingToCommit
+ return "", nil // TODO(gio): maybe return ErrorNothingToCommit
}
- if _, err := wt.Commit(message, &git.CommitOptions{
+ hash, err := wt.Commit(message, &git.CommitOptions{
Author: &object.Signature{
Name: "pcloud-installer",
When: time.Now(),
},
- }); err != nil {
- return err
+ })
+ if err != nil {
+ return "", err
}
gopts := &git.PushOptions{
RemoteName: "origin",
@@ -227,10 +228,10 @@
if o.Force {
gopts.Force = true
}
- return r.repo.Push(gopts)
+ return hash.String(), r.repo.Push(gopts)
}
-func (r *repoIO) Do(op DoFn, opts ...DoOption) error {
+func (r *repoIO) Do(op DoFn, opts ...DoOption) (string, error) {
o := &doOptions{}
for _, i := range opts {
i(o)
@@ -241,15 +242,15 @@
}
if !o.NoPull {
if err := r.pullWithoutLock(); err != nil {
- return err
+ return "", err
}
}
msg, err := op(r)
if err != nil {
- return err
+ return "", err
}
if o.NoCommit {
- return nil
+ return "", nil
}
popts := []PushOption{}
if o.Force {
diff --git a/core/installer/tasks/activate.go b/core/installer/tasks/activate.go
index 6916262..dafbaf7 100644
--- a/core/installer/tasks/activate.go
+++ b/core/installer/tasks/activate.go
@@ -32,7 +32,7 @@
return err
}
repoHost := strings.Split(st.ssClient.Address(), ":")[0]
- return st.repo.Do(func(r soft.RepoFS) (string, error) {
+ _, err = st.repo.Do(func(r soft.RepoFS) (string, error) {
kust, err := soft.ReadKustomization(r, "environments/kustomization.yaml")
if err != nil {
return "", err
@@ -69,6 +69,7 @@
}
return fmt.Sprintf("%s: initialize environment", env.Id), nil
})
+ return err
})
return &t
}
diff --git a/core/installer/tasks/infra.go b/core/installer/tasks/infra.go
index eb4840f..9d0c011 100644
--- a/core/installer/tasks/infra.go
+++ b/core/installer/tasks/infra.go
@@ -88,13 +88,14 @@
if err != nil {
return err
}
- return r.Do(func(r soft.RepoFS) (string, error) {
+ _, err = r.Do(func(r soft.RepoFS) (string, error) {
fa := firstAccount{false, env.Domain, initGroups}
if err := soft.WriteYaml(r, "first-account.yaml", fa); err != nil {
return "", err
}
return "first account membership configuration", nil
})
+ return err
})
return &t
}
diff --git a/core/installer/tasks/init.go b/core/installer/tasks/init.go
index f55344d..30e35e0 100644
--- a/core/installer/tasks/init.go
+++ b/core/installer/tasks/init.go
@@ -102,7 +102,7 @@
if err != nil {
return err
}
- if err := repoIO.Do(func(r soft.RepoFS) (string, error) {
+ if _, err := repoIO.Do(func(r soft.RepoFS) (string, error) {
w, err := r.Writer("README.md")
if err != nil {
return "", err
diff --git a/core/installer/welcome/dodo-app-tmpl/app_status.html b/core/installer/welcome/dodo-app-tmpl/app_status.html
index efe74a1..eabc5b2 100644
--- a/core/installer/welcome/dodo-app-tmpl/app_status.html
+++ b/core/installer/welcome/dodo-app-tmpl/app_status.html
@@ -2,10 +2,9 @@
dodo app: {{ .Name }}
{{ end }}
{{- define "content" -}}
-<a href="/">Home</a><br/><br/>
{{ .GitCloneCommand }}
<hr class="divider">
{{- range .Commits -}}
-{{if eq .Status "OK" }}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="black" d="M21 7L9 19l-5.5-5.5l1.41-1.41L9 16.17L19.59 5.59z"/></svg>{{ else }}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 48 48"><path fill="black" fill-rule="evenodd" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M6 11L11 6L24 19L37 6L42 11L29 24L42 37L37 42L24 29L11 42L6 37L19 24L6 11Z" clip-rule="evenodd"/></svg>{{ end }} {{ .Hash }} {{ .Message }}<br/>
+{{if eq .Status "OK" }}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="black" d="M21 7L9 19l-5.5-5.5l1.41-1.41L9 16.17L19.59 5.59z"/></svg>{{ else }}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 48 48"><path fill="black" fill-rule="evenodd" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M6 11L11 6L24 19L37 6L42 11L29 24L42 37L37 42L24 29L11 42L6 37L19 24L6 11Z" clip-rule="evenodd"/></svg>{{ end }} <a href="/{{ $.Name }}/{{ .Hash }}">{{ .Hash }}</a> {{ .Message }}<br/>
{{- end -}}
{{- end -}}
diff --git a/core/installer/welcome/dodo-app-tmpl/base.html b/core/installer/welcome/dodo-app-tmpl/base.html
index b0251cd..6334f41 100644
--- a/core/installer/welcome/dodo-app-tmpl/base.html
+++ b/core/installer/welcome/dodo-app-tmpl/base.html
@@ -8,6 +8,13 @@
<link rel="stylesheet" href="/stat/dodo_app.css?v=0.0.8">
</head>
<body class="container">
+ <nav aria-label="breadcrumb">
+ <ul>
+ {{- range $i := .Navigation }}
+ <li><a href="{{ $i.Address }}">{{- $i.Name -}}</a></li>
+ {{- end }}
+ </ul>
+ </nav>
{{- block "content" . }}
{{- end }}
</body>
diff --git a/core/installer/welcome/dodo-app-tmpl/commit_status.html b/core/installer/welcome/dodo-app-tmpl/commit_status.html
new file mode 100644
index 0000000..8616545
--- /dev/null
+++ b/core/installer/welcome/dodo-app-tmpl/commit_status.html
@@ -0,0 +1,32 @@
+{{ define "title" }}
+dodo app: {{ .AppName }}
+{{ end }}
+{{- define "content" -}}
+{{ if ne .Commit.Error "" }}
+{{ .CommitError }}
+{{ end }}
+{{- if gt (len .Resources.Volume) 0 -}}
+<h2>Volumes</h2>
+{{- range $v := .Resources.Volume -}}
+Name: {{ $v.Name }}<br/>
+Size: {{ $v.Size }}<br/>
+<br/>
+{{- end -}}
+{{- end -}}
+{{- if gt (len .Resources.PostgreSQL) 0 -}}
+<h2>PostgreSQL</h2>
+{{- range $p := .Resources.PostgreSQL -}}
+Name: {{ $p.Name }}<br/>
+Version: {{ $p.Version }}<br/>
+Volume: {{ $p.Volume }}<br/>
+<br/>
+{{- end -}}
+{{- end -}}
+{{- if gt (len .Resources.Ingress) 0 -}}
+<h2>Ingress</h2>
+{{- range $i := .Resources.Ingress -}}
+Host: {{ $i.Host }}<br/>
+<br/>
+{{- end -}}
+{{- end -}}
+{{- end -}}
diff --git a/core/installer/welcome/dodo_app.go b/core/installer/welcome/dodo_app.go
index 7c35d69..43a6bd7 100644
--- a/core/installer/welcome/dodo_app.go
+++ b/core/installer/welcome/dodo_app.go
@@ -42,11 +42,13 @@
apiCreateApp = "/api/apps"
sessionCookie = "dodo-app-session"
userCtx = "user"
+ initCommitMsg = "init"
)
type dodoAppTmplts struct {
- index *template.Template
- appStatus *template.Template
+ index *template.Template
+ appStatus *template.Template
+ commitStatus *template.Template
}
func parseTemplatesDodoApp(fs embed.FS) (dodoAppTmplts, error) {
@@ -69,7 +71,11 @@
if err != nil {
return dodoAppTmplts{}, err
}
- return dodoAppTmplts{index, appStatus}, nil
+ commitStatus, err := parse("dodo-app-tmpl/commit_status.html")
+ if err != nil {
+ return dodoAppTmplts{}, err
+ }
+ return dodoAppTmplts{index, appStatus, commitStatus}, nil
}
type DodoAppServer struct {
@@ -185,6 +191,7 @@
r.HandleFunc(apiCreateApp, s.handleAPICreateApp).Methods(http.MethodPost)
r.HandleFunc("/{app-name}"+loginPath, s.handleLoginForm).Methods(http.MethodGet)
r.HandleFunc("/{app-name}"+loginPath, s.handleLogin).Methods(http.MethodPost)
+ r.HandleFunc("/{app-name}/{hash}", s.handleAppCommit).Methods(http.MethodGet)
r.HandleFunc("/{app-name}", s.handleAppStatus).Methods(http.MethodGet)
r.HandleFunc("/", s.handleStatus).Methods(http.MethodGet)
r.HandleFunc("/", s.handleCreateApp).Methods(http.MethodPost)
@@ -373,10 +380,16 @@
http.Redirect(w, r, fmt.Sprintf("/%s", appName), http.StatusSeeOther)
}
+type navItem struct {
+ Name string
+ Address string
+}
+
type statusData struct {
- Apps []string
- Networks []installer.Network
- Types []string
+ Navigation []navItem
+ Apps []string
+ Networks []installer.Network
+ Types []string
}
func (s *DodoAppServer) handleStatus(w http.ResponseWriter, r *http.Request) {
@@ -399,7 +412,8 @@
for _, t := range s.appTmpls.Types() {
types = append(types, strings.Replace(t, "-", ":", 1))
}
- data := statusData{apps, networks, types}
+ n := []navItem{navItem{"Home", "/"}}
+ data := statusData{n, apps, networks, types}
if err := s.tmplts.index.Execute(w, data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
@@ -407,9 +421,10 @@
}
type appStatusData struct {
+ Navigation []navItem
Name string
GitCloneCommand string
- Commits []Commit
+ Commits []CommitMeta
}
func (s *DodoAppServer) handleAppStatus(w http.ResponseWriter, r *http.Request) {
@@ -444,6 +459,10 @@
return
}
data := appStatusData{
+ Navigation: []navItem{
+ navItem{"Home", "/"},
+ navItem{appName, "/" + appName},
+ },
Name: appName,
GitCloneCommand: fmt.Sprintf("git clone %s/%s\n\n\n", s.repoPublicAddr, appName),
Commits: commits,
@@ -454,6 +473,97 @@
}
}
+type volume struct {
+ Name string
+ Size string
+}
+
+type postgresql struct {
+ Name string
+ Version string
+ Volume string
+}
+
+type ingress struct {
+ Host string
+}
+
+type resourceData struct {
+ Volume []volume
+ PostgreSQL []postgresql
+ Ingress []ingress
+}
+
+type commitStatusData struct {
+ Navigation []navItem
+ AppName string
+ Commit Commit
+ Resources resourceData
+}
+
+func (s *DodoAppServer) handleAppCommit(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ appName, ok := vars["app-name"]
+ if !ok || appName == "" {
+ http.Error(w, "missing app-name", http.StatusBadRequest)
+ return
+ }
+ hash, ok := vars["hash"]
+ if !ok || appName == "" {
+ http.Error(w, "missing app-name", http.StatusBadRequest)
+ return
+ }
+ u := r.Context().Value(userCtx)
+ if u == nil {
+ http.Error(w, "unauthorized", http.StatusUnauthorized)
+ return
+ }
+ user, ok := u.(string)
+ if !ok {
+ http.Error(w, "could not get user", http.StatusInternalServerError)
+ return
+ }
+ owner, err := s.st.GetAppOwner(appName)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ if owner != user {
+ http.Error(w, "unauthorized", http.StatusUnauthorized)
+ return
+ }
+ commit, err := s.st.GetCommit(hash)
+ if err != nil {
+ // TODO(gio): not-found ?
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ var res strings.Builder
+ if err := json.NewEncoder(&res).Encode(commit.Resources.Helm); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ resData, err := extractResourceData(commit.Resources.Helm)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ data := commitStatusData{
+ Navigation: []navItem{
+ navItem{"Home", "/"},
+ navItem{appName, "/" + appName},
+ navItem{hash, "/" + appName + "/" + hash},
+ },
+ AppName: appName,
+ Commit: commit,
+ Resources: resData,
+ }
+ if err := s.tmplts.commitStatus.Execute(w, data); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
type apiUpdateReq struct {
Ref string `json:"ref"`
Repository struct {
@@ -508,16 +618,11 @@
fmt.Printf("Error: could not find commit message")
return
}
- if err := s.updateDodoApp(instanceAppStatus, req.Repository.Name, s.appConfigs[req.Repository.Name].Namespace, networks); err != nil {
+ resources, err := s.updateDodoApp(instanceAppStatus, req.Repository.Name, s.appConfigs[req.Repository.Name].Namespace, networks)
+ if err = s.createCommit(req.Repository.Name, req.After, commitMsg, err, resources); err != nil {
fmt.Printf("Error: %s\n", err.Error())
- if err := s.st.CreateCommit(req.Repository.Name, req.After, commitMsg, err.Error()); err != nil {
- fmt.Printf("Error: %s\n", err.Error())
- }
return
}
- if err := s.st.CreateCommit(req.Repository.Name, req.After, commitMsg, "OK"); err != nil {
- fmt.Printf("Error: %s\n", err.Error())
- }
for addr, _ := range s.workers[req.Repository.Name] {
go func() {
// TODO(gio): make port configurable
@@ -710,7 +815,8 @@
if err != nil {
return err
}
- if err := s.initRepo(appRepo, appType, n, subdomain); err != nil {
+ commit, err := s.initRepo(appRepo, appType, n, subdomain)
+ if err != nil {
return err
}
apps := installer.NewInMemoryAppRepository(installer.CreateAllApps())
@@ -729,7 +835,12 @@
}
namespace := fmt.Sprintf("%s%s%s", s.env.NamespacePrefix, instanceApp.Namespace(), suffix)
s.appConfigs[appName] = appConfig{namespace, network}
- if err := s.updateDodoApp(instanceAppStatus, appName, namespace, networks); err != nil {
+ resources, err := s.updateDodoApp(instanceAppStatus, appName, namespace, networks)
+ if err != nil {
+ return err
+ }
+ if err = s.createCommit(appName, commit, initCommitMsg, err, resources); err != nil {
+ fmt.Printf("Error: %s\n", err.Error())
return err
}
configRepo, err := s.client.GetRepo(ConfigRepoName)
@@ -741,7 +852,7 @@
if err != nil {
return err
}
- if err := configRepo.Do(func(fs soft.RepoFS) (string, error) {
+ _, err = configRepo.Do(func(fs soft.RepoFS) (string, error) {
w, err := fs.Writer(appConfigsFile)
if err != nil {
return "", err
@@ -768,7 +879,8 @@
return "", err
}
return fmt.Sprintf("Installed app: %s", appName), nil
- }); err != nil {
+ })
+ if err != nil {
return err
}
cfg, err := m.FindInstance(appName)
@@ -849,32 +961,33 @@
} `json:"input"`
}
-func (s *DodoAppServer) updateDodoApp(appStatus installer.EnvApp, name, namespace string, networks []installer.Network) error {
+func (s *DodoAppServer) updateDodoApp(appStatus installer.EnvApp, name, namespace string, networks []installer.Network) (installer.ReleaseResources, error) {
fmt.Println("111")
repo, err := s.client.GetRepo(name)
if err != nil {
- return err
+ return installer.ReleaseResources{}, err
}
fmt.Println("111")
hf := installer.NewGitHelmFetcher()
m, err := installer.NewAppManager(repo, s.nsc, s.jc, hf, "/.dodo")
if err != nil {
- return err
+ return installer.ReleaseResources{}, err
}
fmt.Println("111")
appCfg, err := soft.ReadFile(repo, "app.cue")
if err != nil {
- return err
+ return installer.ReleaseResources{}, err
}
fmt.Println("111")
app, err := installer.NewDodoApp(appCfg)
if err != nil {
- return err
+ return installer.ReleaseResources{}, err
}
fmt.Println("111")
lg := installer.GitRepositoryLocalChartGenerator{"app", namespace}
- return repo.Do(func(r soft.RepoFS) (string, error) {
- res, err := m.Install(
+ var ret installer.ReleaseResources
+ if _, err := repo.Do(func(r soft.RepoFS) (string, error) {
+ ret, err = m.Install(
app,
"app",
"/.dodo/app",
@@ -898,7 +1011,7 @@
}
fmt.Println("111")
var rendered dodoAppRendered
- if err := json.NewDecoder(bytes.NewReader(res.RenderedRaw)).Decode(&rendered); err != nil {
+ if err := json.NewDecoder(bytes.NewReader(ret.RenderedRaw)).Decode(&rendered); err != nil {
return "", nil
}
fmt.Println("111")
@@ -926,20 +1039,23 @@
},
soft.WithCommitToBranch("dodo"),
soft.WithForce(),
- )
+ ); err != nil {
+ return installer.ReleaseResources{}, err
+ }
+ return ret, nil
}
-func (s *DodoAppServer) initRepo(repo soft.RepoIO, appType string, network installer.Network, subdomain string) error {
+func (s *DodoAppServer) initRepo(repo soft.RepoIO, appType string, network installer.Network, subdomain string) (string, error) {
appType = strings.Replace(appType, ":", "-", 1)
appTmpl, err := s.appTmpls.Find(appType)
if err != nil {
- return err
+ return "", err
}
return repo.Do(func(fs soft.RepoFS) (string, error) {
if err := appTmpl.Render(network, subdomain, repo); err != nil {
return "", err
}
- return "init", nil
+ return initCommitMsg, nil
})
}
@@ -994,6 +1110,30 @@
}
}
+func (s *DodoAppServer) createCommit(name, hash, message string, err error, resources installer.ReleaseResources) error {
+ if err != nil {
+ fmt.Printf("Error: %s\n", err.Error())
+ if err := s.st.CreateCommit(name, hash, message, "FAILED", err.Error(), nil); err != nil {
+ fmt.Printf("Error: %s\n", err.Error())
+ return err
+ }
+ return err
+ }
+ var resB bytes.Buffer
+ if err := json.NewEncoder(&resB).Encode(resources); err != nil {
+ if err := s.st.CreateCommit(name, hash, message, "FAILED", err.Error(), nil); err != nil {
+ fmt.Printf("Error: %s\n", err.Error())
+ return err
+ }
+ return err
+ }
+ if err := s.st.CreateCommit(name, hash, message, "OK", "", resB.Bytes()); err != nil {
+ fmt.Printf("Error: %s\n", err.Error())
+ return err
+ }
+ return nil
+}
+
func pickNetwork(networks []installer.Network, network string) []installer.Network {
for _, n := range networks {
if n.Name == network {
@@ -1194,3 +1334,48 @@
}
}
}
+
+func extractResourceData(resources []installer.Resource) (resourceData, error) {
+ var ret resourceData
+ for _, r := range resources {
+ t, ok := r.Annotations["dodo.cloud/resource-type"]
+ if !ok {
+ continue
+ }
+ switch t {
+ case "volume":
+ name, ok := r.Annotations["dodo.cloud/resource.volume.name"]
+ if !ok {
+ return resourceData{}, fmt.Errorf("no name")
+ }
+ size, ok := r.Annotations["dodo.cloud/resource.volume.size"]
+ if !ok {
+ return resourceData{}, fmt.Errorf("no size")
+ }
+ ret.Volume = append(ret.Volume, volume{name, size})
+ case "postgresql":
+ name, ok := r.Annotations["dodo.cloud/resource.postgresql.name"]
+ if !ok {
+ return resourceData{}, fmt.Errorf("no name")
+ }
+ version, ok := r.Annotations["dodo.cloud/resource.postgresql.version"]
+ if !ok {
+ return resourceData{}, fmt.Errorf("no version")
+ }
+ volume, ok := r.Annotations["dodo.cloud/resource.postgresql.volume"]
+ if !ok {
+ return resourceData{}, fmt.Errorf("no volume")
+ }
+ ret.PostgreSQL = append(ret.PostgreSQL, postgresql{name, version, volume})
+ case "ingress":
+ host, ok := r.Annotations["dodo.cloud/resource.ingress.host"]
+ if !ok {
+ return resourceData{}, fmt.Errorf("no host")
+ }
+ ret.Ingress = append(ret.Ingress, ingress{host})
+ default:
+ fmt.Printf("Unknown resource: %+v\n", r.Annotations)
+ }
+ }
+ return ret, nil
+}
diff --git a/core/installer/welcome/env.go b/core/installer/welcome/env.go
index 356e47c..82fb3d5 100644
--- a/core/installer/welcome/env.go
+++ b/core/installer/welcome/env.go
@@ -280,7 +280,8 @@
return err
}
}
- return s.repo.CommitAndPush("Generated new invitation")
+ _, err = s.repo.CommitAndPush("Generated new invitation")
+ return err
}
func extractRequest(r *http.Request) (createEnvReq, error) {
@@ -380,7 +381,7 @@
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
- if err := s.repo.CommitAndPush(fmt.Sprintf("Allocate CIDR for %s", req.Name)); err != nil {
+ if _, err := s.repo.CommitAndPush(fmt.Sprintf("Allocate CIDR for %s", req.Name)); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
diff --git a/core/installer/welcome/env_test.go b/core/installer/welcome/env_test.go
index 040607d..e7e75a8 100644
--- a/core/installer/welcome/env_test.go
+++ b/core/installer/welcome/env_test.go
@@ -76,17 +76,17 @@
return nil
}
-func (r mockRepoIO) CommitAndPush(message string, opts ...soft.PushOption) error {
+func (r mockRepoIO) CommitAndPush(message string, opts ...soft.PushOption) (string, error) {
r.t.Logf("Commit and push: %s", message)
- return nil
+ return "", nil
}
-func (r mockRepoIO) Do(op soft.DoFn, _ ...soft.DoOption) error {
+func (r mockRepoIO) Do(op soft.DoFn, _ ...soft.DoOption) (string, error) {
r.l.Lock()
defer r.l.Unlock()
msg, err := op(r)
if err != nil {
- return err
+ return "", err
}
return r.CommitAndPush(msg)
}
diff --git a/core/installer/welcome/store.go b/core/installer/welcome/store.go
index 031cdff..06be06a 100644
--- a/core/installer/welcome/store.go
+++ b/core/installer/welcome/store.go
@@ -1,11 +1,14 @@
package welcome
import (
+ "bytes"
"database/sql"
+ "encoding/json"
"errors"
"github.com/ncruces/go-sqlite3"
+ "github.com/giolekva/pcloud/core/installer"
"github.com/giolekva/pcloud/core/installer/soft"
)
@@ -17,12 +20,18 @@
ErrorAlreadyExists = errors.New("already exists")
)
-type Commit struct {
- Hash string
+type CommitMeta struct {
Status string
+ Error string
+ Hash string
Message string
}
+type Commit struct {
+ CommitMeta
+ Resources installer.ReleaseResources
+}
+
type Store interface {
CreateUser(username string, password []byte, network string) error
GetUserPassword(username string) ([]byte, error)
@@ -31,8 +40,9 @@
GetUserApps(username string) ([]string, error)
CreateApp(name, username string) error
GetAppOwner(name string) (string, error)
- CreateCommit(name, hash, message, status string) error
- GetCommitHistory(name string) ([]Commit, error)
+ CreateCommit(name, hash, message, status, error string, resources []byte) error
+ GetCommitHistory(name string) ([]CommitMeta, error)
+ GetCommit(hash string) (Commit, error)
}
func NewStore(cf soft.RepoIO, db *sql.DB) (Store, error) {
@@ -63,7 +73,9 @@
app_name TEXT,
hash TEXT,
message TEXT,
- status TEXT
+ status TEXT,
+ error TEXT,
+ resources JSONB
);
`)
return err
@@ -174,26 +186,26 @@
return ret, nil
}
-func (s *storeImpl) CreateCommit(name, hash, message, status string) error {
- query := `INSERT INTO commits (app_name, hash, message, status) VALUES (?, ?, ?, ?)`
- _, err := s.db.Exec(query, name, hash, message, status)
+func (s *storeImpl) CreateCommit(name, hash, message, status, error string, resources []byte) error {
+ query := `INSERT INTO commits (app_name, hash, message, status, error, resources) VALUES (?, ?, ?, ?, ?, ?)`
+ _, err := s.db.Exec(query, name, hash, message, status, error, resources)
return err
}
-func (s *storeImpl) GetCommitHistory(name string) ([]Commit, error) {
- query := `SELECT hash, message, status FROM commits WHERE app_name = ?`
+func (s *storeImpl) GetCommitHistory(name string) ([]CommitMeta, error) {
+ query := `SELECT hash, message, status, error FROM commits WHERE app_name = ?`
rows, err := s.db.Query(query, name)
if err != nil {
return nil, err
}
defer rows.Close()
- ret := []Commit{}
+ ret := []CommitMeta{}
for rows.Next() {
if err := rows.Err(); err != nil {
return nil, err
}
- var c Commit
- if err := rows.Scan(&c.Hash, &c.Message, &c.Status); err != nil {
+ var c CommitMeta
+ if err := rows.Scan(&c.Hash, &c.Message, &c.Status, &c.Error); err != nil {
return nil, err
}
ret = append(ret, c)
@@ -201,3 +213,21 @@
}
return ret, nil
}
+
+func (s *storeImpl) GetCommit(hash string) (Commit, error) {
+ query := `SELECT hash, message, status, error, resources FROM commits WHERE hash = ?`
+ row := s.db.QueryRow(query, hash)
+ if err := row.Err(); err != nil {
+ return Commit{}, err
+ }
+ var ret Commit
+ var c Commit
+ var res []byte
+ if err := row.Scan(&c.Hash, &c.Message, &c.Status, &c.Error, &res); err != nil {
+ return Commit{}, err
+ }
+ if err := json.NewDecoder(bytes.NewBuffer(res)).Decode(&ret.Resources); err != nil {
+ return Commit{}, err
+ }
+ return ret, nil
+}
diff --git a/core/installer/welcome/welcome.go b/core/installer/welcome/welcome.go
index 234a53b..9e45de5 100644
--- a/core/installer/welcome/welcome.go
+++ b/core/installer/welcome/welcome.go
@@ -266,7 +266,7 @@
}
func (s *Server) createUser(username string) error {
- return s.repo.Do(func(r soft.RepoFS) (string, error) {
+ _, err := s.repo.Do(func(r soft.RepoFS) (string, error) {
var fa firstAccount
if err := soft.ReadYaml(r, "first-account.yaml", &fa); err != nil {
return "", err
@@ -311,4 +311,5 @@
}
return "initialized groups for first account", nil
})
+ return err
}