Skip to content

Commit

Permalink
feat(ui): install and uninstall trivial packages #29 #33
Browse files Browse the repository at this point in the history
  • Loading branch information
christophenne authored and kosmoz committed Jan 30, 2024
1 parent 620d7ea commit 975f2fb
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 43 deletions.
2 changes: 1 addition & 1 deletion cmd/glasskube/cmd/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ var installCmd = &cobra.Command{
ValidArgsFunction: completeAvailablePackageNames,
Run: func(cmd *cobra.Command, args []string) {
client := client.FromContext(cmd.Context())
status, err := install.Install(client, cmd.Context(), args[0])
status, err := install.InstallBlocking(client, cmd.Context(), args[0])
if err != nil {
fmt.Fprintf(os.Stderr, "An error occurred during installation:\n\n%v\n", err)
os.Exit(1)
Expand Down
17 changes: 14 additions & 3 deletions cmd/glasskube/cmd/uninstall.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/glasskube/glasskube/internal/cliutils"
"github.com/glasskube/glasskube/internal/config"
"github.com/glasskube/glasskube/pkg/client"
"github.com/glasskube/glasskube/pkg/list"
"github.com/glasskube/glasskube/pkg/uninstall"
"github.com/spf13/cobra"
)
Expand All @@ -19,13 +20,23 @@ var uninstallCmd = &cobra.Command{
PreRun: cliutils.SetupClientContext,
Run: func(cmd *cobra.Command, args []string) {
client := client.FromContext(cmd.Context())
ok, err := uninstall.Uninstall(client, cmd.Context(), args[0], config.ForceUninstall)
pkgName := args[0]
pkg, err := list.Get(client, cmd.Context(), pkgName)
if err != nil {
fmt.Fprintf(os.Stderr, "An error occurred during uninstallation:\n\n%v\n", err)
fmt.Fprintf(os.Stderr, "Could not get installed package %v:\n%v\n", pkgName, err)
os.Exit(1)
return
}
if ok {
proceed := config.ForceUninstall || cliutils.YesNoPrompt(
fmt.Sprintf("%v will be removed from your cluster. Are you sure?", pkgName), false)
if proceed {
fmt.Printf("Uninstalling %v.\n", pkgName)
err = uninstall.Uninstall(client, cmd.Context(), pkg)
if err != nil {
fmt.Fprintf(os.Stderr, "An error occurred during uninstallation:\n\n%v\n", err)
os.Exit(1)
return
}
fmt.Println("Uninstalled successfully.")
} else {
fmt.Println("Uninstallation cancelled.")
Expand Down
7 changes: 7 additions & 0 deletions internal/web/root/static/js/bootstrap.bundle.min.js

Large diffs are not rendered by default.

35 changes: 32 additions & 3 deletions internal/web/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import (
"embed"
"fmt"
"github.com/glasskube/glasskube/pkg/client"
"github.com/glasskube/glasskube/pkg/install"
"github.com/glasskube/glasskube/pkg/list"
"github.com/glasskube/glasskube/pkg/uninstall"
"html/template"
"io/fs"
"k8s.io/apimachinery/pkg/api/errors"
"net/http"
"os"
"os/exec"
Expand All @@ -27,18 +30,44 @@ func Start(ctx context.Context) error {
return err
}

root, _ := fs.Sub(embededFs, "root")
root, err := fs.Sub(embededFs, "root")
if err != nil {
return err
}
fileServer := http.FileServer(http.FS(root))
http.Handle("/static/", fileServer)
http.Handle("/favicon.ico", fileServer)
http.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// TODO check whether to show kubeconfig helper page + reload button #31

pkgClient := client.FromContext(ctx)
if r.Method == "POST" {
pkgName := r.FormValue("packageName")
pkg, err := list.Get(pkgClient, ctx, pkgName)
if err != nil && !errors.IsNotFound(err) {
fmt.Fprintf(os.Stderr, "%v\n", err)
return
}
if pkg != nil {
err := uninstall.Uninstall(pkgClient, ctx, pkg)
if err != nil {
fmt.Fprintf(os.Stderr, "An error occured uninstalling %v: \n%v\n", pkgName, err)
}
http.Redirect(w, r, "/", http.StatusFound)
} else {
_, err := install.Install(pkgClient, ctx, pkgName)
if err != nil {
fmt.Fprintf(os.Stderr, "An error occured installing %v: \n%v\n", pkgName, err)
}
http.Redirect(w, r, "/", http.StatusFound)
}
return
}

packages, _ := list.GetPackagesWithStatus(pkgClient, ctx, false)
err := tmpl.Execute(w, packages)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
fmt.Fprintf(os.Stderr, "An error occured rendering the response: \n%v\n", err)
}
})

Expand Down
37 changes: 28 additions & 9 deletions internal/web/templates/packages.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ <h1 class="navbar-brand mx-1 my-0">Glasskube</h1>
</div>
</nav>

<div class="container-lg mt-4">
<form action="/" method="post">
<div class="container-lg mt-4">
<div class="row row-cols-3 row-cols-xl-4 g-2">
{{range .}}
<div class="col">
Expand All @@ -68,23 +69,41 @@ <h6 class="text-dark m-0">{{ .PackageName }}</h6>
</div>
</div>
{{if eq .Status nil}}
<button type="button" class="btn btn-primary btn-sm fw-medium m-1 mt-auto">Install</button>
<button type="submit" name="packageName" value="{{ .PackageName }}" class="btn btn-primary btn-sm fw-medium m-1 mt-auto">Install</button>
{{else if eq .Status.Status "Pending"}}
<button type="button" class="btn btn-primary btn-sm fw-medium m-1 mt-auto" disabled>Pending</button>
<button type="button" class="btn btn-primary btn-sm fw-medium m-1 mt-auto" disabled>Pending - Refresh to update</button>
{{else if eq .Status.Status "Failed"}}
<button type="button" class="btn btn-danger btn-sm fw-medium m-1 mt-auto" disabled>Installation Failed</button>
<div class="btn-group fw-medium m-1 mt-auto">
<button type="button" class="btn btn-danger btn-sm dropdown-toggle" data-bs-toggle="dropdown">
<span>Installation Failed</span>
</button>
<ul class="dropdown-menu">
<li><button class="dropdown-item btn btn-sm"
onclick="if(!confirm('Do you really want to uninstall {{ .PackageName }}?')) { event.preventDefault() }"
type="submit" name="packageName" value="{{ .PackageName }}">Uninstall {{ .PackageName }}</button></li>
</ul>
</div>
{{else}}
<button type="button" class="btn btn-success btn-sm fw-medium m-1 mt-auto">
<i class="bi bi-check-lg"></i>
<span>Installed</span>
</button>
<div class="btn-group fw-medium m-1 mt-auto">
<button type="button" class="btn btn-success btn-sm dropdown-toggle" data-bs-toggle="dropdown">
<i class="bi bi-check-lg"></i>
<span>Installed</span>
</button>
<ul class="dropdown-menu">
<li><button class="dropdown-item btn btn-sm"
onclick="if(!confirm('Do you really want to uninstall {{ .PackageName }}?')) { event.preventDefault() }"
type="submit" name="packageName" value="{{ .PackageName }}">Uninstall {{ .PackageName }}</button></li>
</ul>
</div>
{{end}}
</div>
</div>
</div>
{{end}}
</div>
</div>
</form>

</div>
<script src="static/js/bootstrap.bundle.min.js"></script>
</body>
</html>
22 changes: 15 additions & 7 deletions pkg/install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,32 @@ import (
"k8s.io/apimachinery/pkg/watch"
)

// Install creates a new v1alpha1.Package custom resource in the cluster, and blocks until this resource has either
// status Ready or Failed.
func Install(pkgClient *client.PackageV1Alpha1Client, ctx context.Context, packageName string) (*client.PackageStatus, error) {
pkg := client.NewPackage(packageName)
err := pkgClient.Packages().Create(ctx, pkg)
// InstallBlocking creates a new v1alpha1.Package custom resource in the cluster and waits until
// the package has either status Ready or Failed.
func InstallBlocking(pkgClient *client.PackageV1Alpha1Client, ctx context.Context, packageName string) (*client.PackageStatus, error) {
pkg, err := Install(pkgClient, ctx, packageName)
if err != nil {
return nil, err
}
fmt.Printf("Installing %v.\n", packageName)

status, err := awaitInstall(pkgClient, ctx, pkg.GetUID())
if err != nil {
return nil, err
}

return status, nil
}

// Install creates a new v1alpha1.Package custom resource in the cluster.
func Install(pkgClient *client.PackageV1Alpha1Client, ctx context.Context, packageName string) (*v1alpha1.Package, error) {
pkg := client.NewPackage(packageName)
err := pkgClient.Packages().Create(ctx, pkg)
if err != nil {
return nil, err
}
fmt.Printf("Installing %v.\n", packageName)
return pkg, err
}

func awaitInstall(pkgClient *client.PackageV1Alpha1Client, ctx context.Context, pkgUID types.UID) (*client.PackageStatus, error) {
watcher, err := pkgClient.Packages().Watch(ctx)
if err != nil {
Expand Down
9 changes: 9 additions & 0 deletions pkg/list/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ type PackageTeaserWithStatus struct {
IconUrl string
}

func Get(client *client.PackageV1Alpha1Client, ctx context.Context, name string) (*v1alpha1.Package, error) {
var pkg v1alpha1.Package
err := client.Packages().Get(ctx, name, &pkg)
if err != nil {
return nil, err
}
return &pkg, nil
}

func GetInstalled(client *client.PackageV1Alpha1Client, ctx context.Context) (*v1alpha1.PackageList, error) {
ls := &v1alpha1.PackageList{}
err := client.Packages().GetAll(ctx, ls)
Expand Down
24 changes: 4 additions & 20 deletions pkg/uninstall/uninstall.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,14 @@ package uninstall

import (
"context"
"fmt"

"github.com/glasskube/glasskube/api/v1alpha1"
"github.com/glasskube/glasskube/internal/cliutils"
"github.com/glasskube/glasskube/pkg/client"
)

func Uninstall(pkgClient *client.PackageV1Alpha1Client, ctx context.Context, packageName string, force bool) (bool, error) {
pkg := &v1alpha1.Package{}
err := pkgClient.Packages().Get(ctx, packageName, pkg)
if err != nil {
return false, err
}

proceed := force || cliutils.YesNoPrompt(
fmt.Sprintf("%v will be removed from your cluster. Are you sure?", packageName), false)
if !proceed {
return false, nil
}

fmt.Printf("Uninstalling %v.\n", packageName)
err = pkgClient.Packages().Delete(ctx, pkg)
func Uninstall(pkgClient *client.PackageV1Alpha1Client, ctx context.Context, pkg *v1alpha1.Package) error {
err := pkgClient.Packages().Delete(ctx, pkg)
if err != nil {
return false, err
return err
}
return true, nil
return nil
}

0 comments on commit 975f2fb

Please sign in to comment.