Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve setup #73

Merged
merged 2 commits into from
May 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
81 changes: 26 additions & 55 deletions cmd/devserver/main.go
Original file line number Diff line number Diff line change
@@ -1,78 +1,49 @@
package main

import (
"fmt"
"flag"
"net/http"
"os"

"github.com/kubenav/kubenav/pkg/electron"
"github.com/kubenav/kubenav/pkg/electron/kube"
"github.com/kubenav/kubenav/pkg/version"

"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

var (
debug bool
kubeconfig string
sync bool
debug = flag.Bool("debug", false, "Enable debug mode.")
kubeconfig = flag.String("kubeconfig", "", "Optional Kubeconfig file.")
sync = flag.Bool("sync", false, "Sync the changes from kubenav with the used Kubeconfig file.")
)

var rootCmd = &cobra.Command{
Use: "kubenav",
Short: "kubenav - the navigator for your Kubernetes clusters.",
Long: "kubenav - the navigator for your Kubernetes clusters.",
Run: func(cmd *cobra.Command, args []string) {
log := logrus.StandardLogger()

logLevel := logrus.InfoLevel
if debug {
logLevel = logrus.DebugLevel
}

log.SetLevel(logLevel)
log.Infof(version.Info())
log.Infof(version.BuildContext())

client, err := kube.NewClient(kubeconfig)
if err != nil {
log.WithError(err).Fatalf("Could not create Kubernetes client")
}

router := http.NewServeMux()
electron.Register(router, sync, client)
func main() {
// Parse command-line flags.
flag.Parse()

if err := http.ListenAndServe(":14122", router); err != nil {
log.WithError(err).Fatalf("kubenav server died")
}
},
}
// Setup the logger and print the version information.
log := logrus.StandardLogger()

var versionCmd = &cobra.Command{
Use: "version",
Short: "Print version information for kubenav.",
Long: "Print version information for kubenav.",
Run: func(cmd *cobra.Command, args []string) {
v, err := version.Print("kubenav")
if err != nil {
logrus.WithError(err).Fatal("Failed to print version information")
}
logLevel := logrus.InfoLevel
if *debug {
logLevel = logrus.DebugLevel
}

fmt.Fprintln(os.Stdout, v)
},
}
log.SetLevel(logLevel)
log.Infof(version.Info())
log.Infof(version.BuildContext())

func init() {
rootCmd.AddCommand(versionCmd)
// Create the client for the interaction with the Kubernetes API.
client, err := kube.NewClient(*kubeconfig)
if err != nil {
log.WithError(err).Fatalf("Could not create Kubernetes client")
}

rootCmd.PersistentFlags().BoolVar(&debug, "debug", false, "Enable debug mode.")
rootCmd.PersistentFlags().StringVar(&kubeconfig, "kubeconfig", "", "Optional Kubeconfig file.")
rootCmd.PersistentFlags().BoolVar(&sync, "sync", false, "Sync the changes from kubenav with the used Kubeconfig file.")
}
// Register the API for the Electron app and start the server.
router := http.NewServeMux()
electron.Register(router, *sync, client)

func main() {
if err := rootCmd.Execute(); err != nil {
logrus.WithError(err).Fatal("Failed to initialize kubenav")
if err := http.ListenAndServe(":14122", router); err != nil {
log.WithError(err).Fatalf("kubenav server died")
}
}
232 changes: 108 additions & 124 deletions cmd/electron/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package main

import (
"bufio"
"flag"
"fmt"
"net/http"
"os"
"strings"

"github.com/kubenav/kubenav/pkg/electron"
Expand All @@ -16,7 +16,6 @@ import (
bootstrap "github.com/asticode/go-astilectron-bootstrap"
assetfs "github.com/elazarl/go-bindata-assetfs"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

var (
Expand All @@ -26,154 +25,139 @@ var (
)

var (
debug bool
kubeconfig string
sync bool
debug = flag.Bool("debug", false, "Enable debug mode.")
kubeconfig = flag.String("kubeconfig", "", "Optional Kubeconfig file.")
sync = flag.Bool("sync", false, "Sync the changes from kubenav with the used Kubeconfig file.")
)

// Message is the structure of a Server Sent Event, which contains the Event and Data. Server Sent Events are used to
// send actions from the Electron menu to the frontend.
type Message struct {
Event string
Data string
}

var messageChannel = make(chan Message)

var rootCmd = &cobra.Command{
Use: "kubenav",
Short: "kubenav - the navigator for your Kubernetes clusters.",
Long: "kubenav - the navigator for your Kubernetes clusters.",
Run: func(cmd *cobra.Command, args []string) {
log := logrus.StandardLogger()

logLevel := logrus.InfoLevel
if debug {
logLevel = logrus.DebugLevel
}

log.SetLevel(logLevel)
log.Infof(version.Info())
log.Infof(version.BuildContext())

client, err := kube.NewClient(kubeconfig)
if err != nil {
log.WithError(err).Fatalf("Could not create Kubernetes client")
}

go func() {
router := http.NewServeMux()
electron.Register(router, sync, client)

router.HandleFunc("/api/electron", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Connection", "keep-alive")
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Access-Control-Allow-Origin", "*")

for {
select {
case msg := <-messageChannel:
log.Debugf("Received message: %#v", msg)
w.Write([]byte(fmt.Sprintf("event: %s\ndata: %s\n\n", msg.Event, msg.Data)))
w.(http.Flusher).Flush()
case <-r.Context().Done():
return
}
}
})
func main() {
// Parse command-line flags.
flag.Parse()

afs := &assetfs.AssetFS{
Asset: Asset,
AssetDir: AssetDir,
AssetInfo: AssetInfo,
Prefix: "/resources/app",
}
// Setup the logger and print the version information.
log := logrus.StandardLogger()

staticHandler := http.FileServer(afs)
logLevel := logrus.InfoLevel
if *debug {
logLevel = logrus.DebugLevel
}

indexFile, err := afs.Open("index.html")
if err != nil {
log.WithError(err).Fatalf("Could not find index.html file")
}
log.SetLevel(logLevel)
log.Infof(version.Info())
log.Infof(version.BuildContext())

reader := bufio.NewReader(indexFile)
index, _, err := reader.ReadLine()
if err != nil {
log.WithError(err).Fatalf("Could not read index.html file")
}
// Create the client for the interaction with the Kubernetes API.
client, err := kube.NewClient(*kubeconfig)
if err != nil {
log.WithError(err).Fatalf("Could not create Kubernetes client")
}

router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if strings.Contains(r.URL.Path, ".") {
staticHandler.ServeHTTP(w, r)
// Register the API routes for the Electron app. Additional to the devserver we need another rout to handle the
// communication between the Electron menu and the frontend via Server Sent Events. We also have to serve the
// frontend from the embedded assets.
go func() {
router := http.NewServeMux()
electron.Register(router, *sync, client)

// Add route for Server Sent Events. The events are handled via the message channel. Possible events are
// "navigation" and "cluster". These events are handled by the frontend to navigate to another page or to modify
// the cluster context.
router.HandleFunc("/api/electron", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Connection", "keep-alive")
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Access-Control-Allow-Origin", "*")

for {
select {
case msg := <-messageChannel:
log.Debugf("Received message: %#v", msg)
w.Write([]byte(fmt.Sprintf("event: %s\ndata: %s\n\n", msg.Event, msg.Data)))
w.(http.Flusher).Flush()
case <-r.Context().Done():
return
}

fmt.Fprintf(w, string(index))
})

if err := http.ListenAndServe(":14122", router); err != nil {
log.WithError(err).Fatalf("kubenav server died")
}
}()

updateAvailable := checkVersion(version.Version, log)
menuOptions, err := getMenuOptions(sync, updateAvailable, client, log)
if err != nil {
log.WithError(err).Fatalf("Could not create menu")
})

// Load the embedded assets and serve all static files. To serve the index.html file we need to read the content
// of the file first, so we can handle reloads in the frontend for each route.
afs := &assetfs.AssetFS{
Asset: Asset,
AssetDir: AssetDir,
AssetInfo: AssetInfo,
Prefix: "/resources/app",
}

if err := bootstrap.Run(bootstrap.Options{
Asset: Asset,
AssetDir: AssetDir,
AstilectronOptions: astilectron.Options{
AppName: AppName,
AppIconDarwinPath: "resources/icon.icns",
AppIconDefaultPath: "resources/icon.png",
SingleInstance: true,
VersionAstilectron: VersionAstilectron,
VersionElectron: VersionElectron,
},
Debug: debug,
Logger: log,
MenuOptions: menuOptions,
RestoreAssets: RestoreAssets,
Windows: []*bootstrap.Window{{
Homepage: "http://localhost:14122/",
Options: &astilectron.WindowOptions{
Center: astikit.BoolPtr(true),
Height: astikit.IntPtr(920),
Width: astikit.IntPtr(1600),
},
}},
}); err != nil {
log.WithError(err).Fatalf("Running kubenav failed")
staticHandler := http.FileServer(afs)

indexFile, err := afs.Open("index.html")
if err != nil {
log.WithError(err).Fatalf("Could not find index.html file")
}
},
}

var versionCmd = &cobra.Command{
Use: "version",
Short: "Print version information for kubenav.",
Long: "Print version information for kubenav.",
Run: func(cmd *cobra.Command, args []string) {
v, err := version.Print("kubenav")
reader := bufio.NewReader(indexFile)
index, _, err := reader.ReadLine()
if err != nil {
logrus.WithError(err).Fatal("Failed to print version information")
log.WithError(err).Fatalf("Could not read index.html file")
}

fmt.Fprintln(os.Stdout, v)
return
},
}
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if strings.Contains(r.URL.Path, ".") {
staticHandler.ServeHTTP(w, r)
return
}

func init() {
rootCmd.AddCommand(versionCmd)
fmt.Fprintf(w, string(index))
})

rootCmd.PersistentFlags().BoolVar(&debug, "debug", false, "Enable debug mode.")
rootCmd.PersistentFlags().StringVar(&kubeconfig, "kubeconfig", "", "Optional Kubeconfig file.")
rootCmd.PersistentFlags().BoolVar(&sync, "sync", false, "Sync the changes from kubenav with the used Kubeconfig file.")
}
if err := http.ListenAndServe(":14122", router); err != nil {
log.WithError(err).Fatalf("kubenav server died")
}
}()

// Check if a new version is available and create the menu. For the menu we need the sync flag, to write the
// selected context back to the Kubeconfig file, the result from the version check and the Kubernetes client and
// logger.
updateAvailable := checkVersion(version.Version, log)
menuOptions, err := getMenuOptions(*sync, updateAvailable, client, log)
if err != nil {
log.WithError(err).Fatalf("Could not create menu")
}

func main() {
if err := rootCmd.Execute(); err != nil {
logrus.WithError(err).Fatal("Failed to initialize kubenav")
// Run the Electron app via the bootstrapper for astilectron.
if err := bootstrap.Run(bootstrap.Options{
Asset: Asset,
AssetDir: AssetDir,
AstilectronOptions: astilectron.Options{
AppName: AppName,
AppIconDarwinPath: "resources/icon.icns",
AppIconDefaultPath: "resources/icon.png",
SingleInstance: true,
VersionAstilectron: VersionAstilectron,
VersionElectron: VersionElectron,
},
Debug: *debug,
Logger: log,
MenuOptions: menuOptions,
RestoreAssets: RestoreAssets,
Windows: []*bootstrap.Window{{
Homepage: "http://localhost:14122/",
Options: &astilectron.WindowOptions{
Center: astikit.BoolPtr(true),
Height: astikit.IntPtr(920),
Width: astikit.IntPtr(1600),
},
}},
}); err != nil {
log.WithError(err).Fatalf("Running kubenav failed")
}
}
2 changes: 0 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ require (
github.com/json-iterator/go v1.1.9 // indirect
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
github.com/sirupsen/logrus v1.6.0
github.com/spf13/cobra v1.0.0
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 // indirect
golang.org/x/mobile v0.0.0-20200329125638-4c31acba0007 // indirect
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
Expand Down