DodoApp: Implement branch and app delete functionalities.
Change-Id: I8bf6ed30a6274203e73e80f05a4b82896509ecb0
diff --git a/core/installer/app_manager.go b/core/installer/app_manager.go
index 9b28004..4ed21f4 100644
--- a/core/installer/app_manager.go
+++ b/core/installer/app_manager.go
@@ -692,13 +692,28 @@
}
cfg = renderedCfg
r.RemoveAll(instanceDir)
- kustPath := filepath.Join(m.appDirRoot, "kustomization.yaml")
- kust, err := soft.ReadKustomization(r, kustPath)
- if err != nil {
- return "", err
+ prev := ""
+ curr := instanceDir
+ for prev != curr {
+ p := filepath.Dir(curr)
+ n := filepath.Base(curr)
+ kustPath := filepath.Join(p, "kustomization.yaml")
+ kust, err := soft.ReadKustomization(r, kustPath)
+ if err != nil {
+ return "", err
+ }
+ kust.RemoveResources(n)
+ if len(kust.Resources) > 0 {
+ soft.WriteYaml(r, kustPath, kust)
+ break
+ } else {
+ if err := r.RemoveAll(kustPath); err != nil {
+ return "", err
+ }
+ }
+ prev = curr
+ curr = p
}
- kust.RemoveResources(instanceId)
- soft.WriteYaml(r, kustPath, kust)
return fmt.Sprintf("uninstall: %s", instanceId), nil
}); err != nil {
return err
diff --git a/core/installer/soft/client.go b/core/installer/soft/client.go
index 5163f23..338af24 100644
--- a/core/installer/soft/client.go
+++ b/core/installer/soft/client.go
@@ -31,6 +31,8 @@
RepoExists(name string) (bool, error)
GetRepo(name string) (RepoIO, error)
GetRepoBranch(name, branch string) (RepoIO, error)
+ DeleteRepoBranch(name, branch string) error
+ DeleteRepo(name string) error
GetAllRepos() ([]string, error)
GetRepoAddress(name string) string
AddRepository(name string) error
@@ -281,6 +283,18 @@
return err
}
+func (ss *realClient) DeleteRepoBranch(name, branch string) error {
+ log.Printf("Deleting branch %s %s", name, branch)
+ _, err := ss.RunCommand("repo", "branch", "delete", name, branch)
+ return err
+}
+
+func (ss *realClient) DeleteRepo(name string) error {
+ log.Printf("Deleting repo %s", name)
+ _, err := ss.RunCommand("repo", "delete", name)
+ return err
+}
+
type Repository struct {
*git.Repository
Addr RepositoryAddress
diff --git a/core/installer/welcome/dodo-app-tmpl/app_status.html b/core/installer/welcome/dodo-app-tmpl/app_status.html
index 916525e..879d0ec 100644
--- a/core/installer/welcome/dodo-app-tmpl/app_status.html
+++ b/core/installer/welcome/dodo-app-tmpl/app_status.html
@@ -22,4 +22,12 @@
{{- range .Branches -}}
<a href="/{{ $.Name }}/branch/{{ . }}">{{ . }}</a><br/>
{{- end -}}
+{{- if ne .Branch "master" -}}
+<form action="/{{ .Name }}/branch/{{ .Branch }}/delete" method="POST">
+ <button aria-busy="false" type="submit" name="delete">delete branch</button>
+</form>
+{{- end -}}
+<form action="/{{ .Name }}/delete" method="POST">
+ <button aria-busy="false" type="submit" name="delete">delete app</button>
+</form>
{{- end -}}
diff --git a/core/installer/welcome/dodo_app.go b/core/installer/welcome/dodo_app.go
index 256007e..51f38e9 100644
--- a/core/installer/welcome/dodo_app.go
+++ b/core/installer/welcome/dodo_app.go
@@ -236,7 +236,9 @@
r.HandleFunc("/{app-name}/{hash}", s.handleAppCommit).Methods(http.MethodGet)
r.HandleFunc("/{app-name}/dev-branch/create", s.handleCreateDevBranch).Methods(http.MethodPost)
r.HandleFunc("/{app-name}/branch/{branch}", s.handleAppStatus).Methods(http.MethodGet)
+ r.HandleFunc("/{app-name}/branch/{branch}/delete", s.handleBranchDelete).Methods(http.MethodPost)
r.HandleFunc("/{app-name}", s.handleAppStatus).Methods(http.MethodGet)
+ r.HandleFunc("/{app-name}/delete", s.handleAppDelete).Methods(http.MethodPost)
r.HandleFunc("/", s.handleStatus).Methods(http.MethodGet)
r.HandleFunc("/", s.handleCreateApp).Methods(http.MethodPost)
e <- http.ListenAndServe(fmt.Sprintf(":%d", s.port), r)
@@ -466,6 +468,7 @@
type appStatusData struct {
Navigation []navItem
Name string
+ Branch string
GitCloneCommand string
Commits []CommitMeta
LastCommit resourceData
@@ -532,6 +535,7 @@
navItem{appName, "/" + appName},
},
Name: appName,
+ Branch: branch,
GitCloneCommand: fmt.Sprintf("git clone %s/%s\n\n\n", s.repoPublicAddr, appName),
Commits: commits,
LastCommit: lastCommitResources,
@@ -899,7 +903,7 @@
}
branch := r.FormValue("branch")
if branch == "" {
- http.Error(w, "missing network", http.StatusBadRequest)
+ http.Error(w, "missing branch", http.StatusBadRequest)
return
}
if err := s.createDevBranch(appName, "master", branch, user); err != nil {
@@ -909,6 +913,49 @@
http.Redirect(w, r, fmt.Sprintf("/%s/branch/%s", appName, branch), http.StatusSeeOther)
}
+func (s *DodoAppServer) handleBranchDelete(w http.ResponseWriter, r *http.Request) {
+ u := r.Context().Value(userCtx)
+ if u == nil {
+ http.Error(w, "unauthorized", http.StatusUnauthorized)
+ return
+ }
+ vars := mux.Vars(r)
+ appName, ok := vars["app-name"]
+ if !ok || appName == "" {
+ http.Error(w, "missing app-name", http.StatusBadRequest)
+ return
+ }
+ branch, ok := vars["branch"]
+ if !ok || branch == "" {
+ http.Error(w, "missing branch", http.StatusBadRequest)
+ return
+ }
+ if err := s.deleteBranch(appName, branch); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ http.Redirect(w, r, fmt.Sprintf("/%s", appName), http.StatusSeeOther)
+}
+
+func (s *DodoAppServer) handleAppDelete(w http.ResponseWriter, r *http.Request) {
+ u := r.Context().Value(userCtx)
+ if u == nil {
+ http.Error(w, "unauthorized", http.StatusUnauthorized)
+ return
+ }
+ vars := mux.Vars(r)
+ appName, ok := vars["app-name"]
+ if !ok || appName == "" {
+ http.Error(w, "missing app-name", http.StatusBadRequest)
+ return
+ }
+ if err := s.deleteApp(appName); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ http.Redirect(w, r, fmt.Sprintf("/%s", appName), http.StatusSeeOther)
+}
+
type apiCreateAppReq struct {
AppType string `json:"type"`
AdminPublicKey string `json:"adminPublicKey"`
@@ -1042,6 +1089,68 @@
return s.createAppForBranch(appRepo, appName, toBranch, user, network, map[string][]byte{"app.json": branchCfg})
}
+func (s *DodoAppServer) deleteBranch(appName string, branch string) error {
+ appBranch := fmt.Sprintf("dodo_%s", branch)
+ hf := installer.NewGitHelmFetcher()
+ if err := func() error {
+ repo, err := s.client.GetRepoBranch(appName, appBranch)
+ if err != nil {
+ return err
+ }
+ m, err := installer.NewAppManager(repo, s.nsc, s.jc, hf, s.vpnKeyGen, s.cnc, "/.dodo")
+ if err != nil {
+ return err
+ }
+ return m.Remove("app")
+ }(); err != nil {
+ return err
+ }
+ configRepo, err := s.client.GetRepo(ConfigRepoName)
+ if err != nil {
+ return err
+ }
+ m, err := installer.NewAppManager(configRepo, s.nsc, s.jc, hf, s.vpnKeyGen, s.cnc, "/")
+ if err != nil {
+ return err
+ }
+ appPath := fmt.Sprintf("%s/%s", appName, branch)
+ if _, err := configRepo.Do(func(fs soft.RepoFS) (string, error) {
+ if err := m.Remove(appPath); err != nil {
+ return "", err
+ }
+ return fmt.Sprintf("Uninstalled app branch: %s %s", appName, branch), nil
+ }); err != nil {
+ return err
+ }
+ if err := s.client.DeleteRepoBranch(appName, appBranch); err != nil {
+ return err
+ }
+ if branch != "master" {
+ return s.client.DeleteRepoBranch(appName, branch)
+ }
+ return nil
+}
+
+func (s *DodoAppServer) deleteApp(appName string) error {
+ configRepo, err := s.client.GetRepo(ConfigRepoName)
+ if err != nil {
+ return err
+ }
+ branches, err := configRepo.ListDir(fmt.Sprintf("/%s", appName))
+ if err != nil {
+ return err
+ }
+ for _, b := range branches {
+ if !b.IsDir() || strings.HasPrefix(b.Name(), "dodo_") {
+ continue
+ }
+ if err := s.deleteBranch(appName, b.Name()); err != nil {
+ return err
+ }
+ }
+ return s.client.DeleteRepo(appName)
+}
+
func (s *DodoAppServer) createAppForBranch(
repo soft.RepoIO,
appName string,
diff --git a/core/installer/welcome/env_test.go b/core/installer/welcome/env_test.go
index 439fdf2..448f221 100644
--- a/core/installer/welcome/env_test.go
+++ b/core/installer/welcome/env_test.go
@@ -190,6 +190,14 @@
return nil
}
+func (f fakeSoftServeClient) DeleteRepoBranch(_, _ string) error {
+ return nil
+}
+
+func (f fakeSoftServeClient) DeleteRepo(_ string) error {
+ return nil
+}
+
type fakeClientGetter struct {
t *testing.T
envFS billy.Filesystem