This repository has been archived by the owner on Oct 31, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #66 from openhie/strawperson
goinstant desktop app version 1.0.0-beta
- Loading branch information
Showing
28 changed files
with
1,811 additions
and
347 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,53 @@ | ||
# goinstant | ||
|
||
This is a Go app and can be built as a native binary for any operating system. Static assets and templates are built using packr2. | ||
This is a Go desktop app and is provided as a native binary for the AMD64 architecture on Windows, macOS, and Linux. | ||
|
||
## Dev prerequisites | ||
> Warning: This app is not meant to be used for container and cluster management in production or with sensitive data. It is meant for demos, training sessions, and by developers. In production and with sensitive data, administrators should use the purpose-built tools like the Docker and Kubernetes CLIs to manage resources directly and according to best practices which are outside the scope of this app. | ||
## How it Works | ||
|
||
> The prototype of goinstant was CLI only and that is now disabled. If a CLI is desired, it it suggested to use the yarn commands provided for running from the command line. The yarn CLI will be maintained. | ||
The desktop is rendered using web pages. | ||
|
||
* Static assets are bundled into the executable. | ||
* Web pages are served by Go HTTP server. | ||
* Calls from JS go to the Go API, which in turn calls Go functions. While Go apps can easily render and serve pages directly, this separation of concerns exists so that a contributor may add a different Web framework like React or Vue, and build a new frontend while the API remains in Go. | ||
* Go functions generally invoke the shell on the user's OS. There are a few exceptions. | ||
* On init and after accepting the disclaimer, this repo is cloned to the system. (This is done with a git-compatible library, git doesn't need to be installed.) | ||
* The console uses server-side events. These are one-way events sent to the client, not bidirectional like websockets. There is a function which can be called `consoleSender` to send events to the console. | ||
|
||
## Security | ||
|
||
This desktop app is meant as a prototype and may change. This app resides in userspace but it invokes the command line for containers and clusters. The apps it invokes, Docker and Kubernetes CLI, launch and manage containers and may have admin/root privileges. | ||
|
||
Therefore, this app is not meant to be used for container and cluster management in production or with sensitive data. It is meant for demos, training sessions, and by developers. In production and with sensitive data, administrators should use the purpose-built tools like the Docker and Kubernetes CLIs to manage resources directly and according to best practices which are outside the scope of this app. | ||
|
||
## Developers | ||
|
||
### Dev prerequisites | ||
|
||
* Install go, [see here](https://golang.org/doc/install). For Ubuntu you might want to use the go snap package, [see here](https://snapcraft.io/install/go/ubuntu). | ||
* Install packr2: **Outside** of the goinstant folder (so that it doesn't get installed as a module) run: `go get -u github.com/gobuffalo/packr/v2/packr2` | ||
* Add go binaries to you system $PATH, on ubuntu: Add `export PATH=$PATH:$HOME/go/bin` to the end of your ~/.bashrc file. To use this change immediately source it: `source ~/.bashrc` | ||
* Install dependencies, run this from the goinstant folder: `go get` | ||
|
||
## Running and building | ||
### Running | ||
|
||
Static assets and templates are built using pkger. For development, run the app using `pkger && go run *.go`. `pkger` must be run if you change the static assets. | ||
|
||
Run the app using `go run goinstant.go` or build the binary using `go build`. To build releases, create a tag and upload the binaries built. A convenience bash script is included to build binaries. | ||
### Building | ||
|
||
To build releases, create a tag and upload the binaries. A convenience bash script is included to build binaries. | ||
|
||
> Note that ARM builds are not yet supported in Go 1.15, but such builds can be supported in future for Linux and macOS. | ||
Note: this script won't work if you have a `goinstant/data/` folder that gets created when when starting up the docker-compose files through the go app. Delete this first: `sudo rm -r goinstant/data` | ||
|
||
```sh | ||
bash ./buildreleases.sh | ||
git tag 0.0.1 | ||
git push origin 0.0.1 | ||
# then upload the binaries. | ||
# then upload the binaries to GitHub | ||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
GOOS=darwin GOARCH=amd64 packr2 build && mv ./goinstant ./bin/goinstant-macos \ | ||
&& GOOS=linux GOARCH=amd64 packr2 build && mv ./goinstant ./bin/goinstant-linux \ | ||
&& GOOS=windows GOARCH=amd64 packr2 build && mv ./goinstant.exe ./bin/goinstant.exe \ | ||
&& packr2 clean | ||
pkger \ | ||
&& GOOS=darwin GOARCH=amd64 go build && mv ./goinstant ./bin/goinstant-macos \ | ||
&& GOOS=linux GOARCH=amd64 go build && mv ./goinstant ./bin/goinstant-linux \ | ||
&& GOOS=windows GOARCH=amd64 go build && mv ./goinstant.exe ./bin/goinstant.exe \ | ||
&& go clean |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package main | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/r3labs/sse" | ||
) | ||
|
||
func sseHandler(w http.ResponseWriter, r *http.Request) { | ||
// log.Println("new client", r.Header.Get("X-Forwarded-For")) | ||
server.HTTPHandler(w, r) | ||
} | ||
|
||
func consoleSender(server *sse.Server, text string) { | ||
|
||
// fmt.Println(text) | ||
server.Publish("messages", &sse.Event{ | ||
Data: []byte(text), | ||
}) | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
version: "3.3" | ||
|
||
services: | ||
mongo: | ||
image: mongo:3.6 | ||
ports: | ||
- "27017:27017" | ||
restart: unless-stopped | ||
|
||
redis: | ||
image: redis | ||
ports: | ||
- "6379:6379" | ||
restart: unless-stopped | ||
|
||
hearth: | ||
image: intrahealth/hearth:latest | ||
environment: | ||
- mongodb__url=mongodb://mongo/hearth-dev | ||
- logger__level=warning | ||
- authentication__type=disabled | ||
# - idGenerator=uuidv4 <- setting from jembi/health, is untested | ||
ports: | ||
- "3447:3447" | ||
restart: unless-stopped | ||
|
||
facility-registry: | ||
image: intrahealth/facility-registry:latest | ||
environment: | ||
- HEARTH_URL=http://hearth:3447 | ||
- REDIS_HOST=redis | ||
- DB_HOST=mongo | ||
ports: | ||
- "3000:3000" | ||
restart: unless-stopped |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"os" | ||
"path/filepath" | ||
"time" | ||
|
||
"github.com/manifoldco/promptui" | ||
) | ||
|
||
// existDisclaimer detects if the accept_disclaimer file exists in the $HOME/.instant folder. | ||
// If it exists, then the user has accepted and it returns 'success', otherwise 'fail'. | ||
func existDisclaimer() string { | ||
home, _ := os.UserHomeDir() | ||
// must use filepath.join not path.join for windows compat | ||
dotfiles := filepath.Join(home, ".instant") | ||
fileName := filepath.Join(dotfiles, "accept_disclaimer") | ||
status := "success" | ||
if _, err := os.Stat(fileName); os.IsNotExist(err) { | ||
status = "fail" | ||
} | ||
return status | ||
} | ||
|
||
func cliDisclaimer() { | ||
status := existDisclaimer() | ||
if status == "fail" { | ||
pkgerPrint("/templates/disclaimer.txt", "yellow") | ||
prompt := promptui.Select{ | ||
Label: "Do you agree to use this application?", | ||
Items: []string{"Yes", "No", "Quit"}, | ||
} | ||
|
||
_, result, err := prompt.Run() | ||
if err != nil { | ||
fmt.Printf("Prompt failed %v\n", err) | ||
return | ||
} | ||
fmt.Printf("You chose %q\n", result) | ||
|
||
switch result { | ||
case "Yes": | ||
makeDisclaimer() | ||
setup() | ||
selectSetup() | ||
case "No": | ||
fmt.Println("Understood. Exiting.") | ||
os.Exit(1) | ||
case "Quit": | ||
os.Exit(1) | ||
} | ||
} else { | ||
selectSetup() | ||
} | ||
} | ||
|
||
func makeDisclaimer() string { | ||
home, _ := os.UserHomeDir() | ||
dotfiles := filepath.Join(home, ".instant") | ||
fileName := filepath.Join(dotfiles, "accept_disclaimer") | ||
_, err := os.Stat(fileName) | ||
status := "success" | ||
if os.IsNotExist(err) { | ||
err := os.MkdirAll(dotfiles, os.ModePerm) | ||
if err != nil { | ||
status = "fail" | ||
log.Fatal(err) | ||
} | ||
file, err := os.Create(fileName) | ||
if err != nil { | ||
status = "fail" | ||
log.Fatal(err) | ||
} | ||
defer file.Close() | ||
} else { | ||
currentTime := time.Now().Local() | ||
err = os.Chtimes(fileName, currentTime, currentTime) | ||
if err != nil { | ||
status = "fail" | ||
fmt.Println(err) | ||
} | ||
} | ||
return status | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
package main | ||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
"fmt" | ||
"log" | ||
"net/http" | ||
"os" | ||
"os/exec" | ||
"runtime" | ||
"strconv" | ||
|
||
"github.com/docker/docker/api/types" | ||
"github.com/docker/docker/client" | ||
"github.com/gookit/color" | ||
"golang.org/x/net/context" | ||
) | ||
|
||
func debugDocker() { | ||
|
||
consoleSender(server, "...checking your Docker setup") | ||
|
||
cwd, err := os.Getwd() | ||
if err != nil { | ||
consoleSender(server, "Can't get current working directory... this is not a great error.") | ||
// panic(err) | ||
} else { | ||
consoleSender(server, cwd) | ||
} | ||
|
||
// ctx := context.Background() | ||
cli, err := client.NewEnvClient() | ||
if err != nil { | ||
// panic(err) | ||
} | ||
|
||
info, err := cli.Info(context.Background()) | ||
if err != nil { | ||
consoleSender(server, "Unable to get Docker context. Please ensure that Docker is downloaded and running") | ||
// panic(err) | ||
} else { | ||
// Docker default is 2GB, which may need to be revisited if Instant grows. | ||
str1 := " bytes memory is allocated.\n" | ||
str2 := strconv.FormatInt(info.MemTotal, 10) | ||
result := str2 + str1 | ||
consoleSender(server, result) | ||
consoleSender(server, "Docker setup looks good") | ||
} | ||
|
||
} | ||
|
||
// TODO: change printf to consoleSender | ||
// listDocker may be used in future | ||
func listDocker() { | ||
|
||
consoleSender(server, "Listing containers") | ||
|
||
// ctx := context.Background() | ||
cli, err := client.NewEnvClient() | ||
if err != nil { | ||
// panic(err) | ||
} | ||
|
||
containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{}) | ||
if err != nil { | ||
consoleSender(server, "Unable to list Docker containers. Please ensure that Docker is downloaded and running") | ||
// return | ||
} | ||
|
||
for _, container := range containers { | ||
items := fmt.Sprintf("ContainerID: %s Status: %s Image: %s\n", container.ID[:10], container.State, container.Image) | ||
consoleSender(server, items) | ||
} | ||
|
||
} | ||
|
||
// SomeStuff is the one func to rule them all | ||
func SomeStuff(r *http.Request) { | ||
consoleSender(server, "Note: Initial setup takes 1-5 minutes. wait for the DONE message") | ||
runner := r.URL.Query().Get("runner") | ||
pk := r.URL.Query().Get("package") | ||
state := r.URL.Query().Get("state") | ||
consoleSender(server, "Runner requested: "+runner) | ||
consoleSender(server, "Package requested: "+pk) | ||
consoleSender(server, "State requested: "+state) | ||
home, _ := os.UserHomeDir() | ||
|
||
// args := []string{runner, "ever", "you", "like"} | ||
// cmd := exec.Command(app, args...) | ||
// consoleSender(server, args[0]) | ||
|
||
cmd := exec.Command("docker", "run", "--rm", "-v", "/var/run/docker.sock:/var/run/docker.sock", "-v", home+"/.kube/config:/root/.kube/config:ro", "-v", home+"/.minikube:/home/$USER/.minikube:ro", "--mount=type=volume,src=instant,dst=/instant", "--network", "host", "openhie/instant:latest", state, "-t", runner, pk) | ||
// create a pipe for the output of the script | ||
cmdReader, err := cmd.StdoutPipe() | ||
if err != nil { | ||
fmt.Fprintln(os.Stderr, "Error creating StdoutPipe for Cmd", err) | ||
return | ||
} | ||
|
||
scanner := bufio.NewScanner(cmdReader) | ||
go func() { | ||
for scanner.Scan() { | ||
// fmt.Printf("\t > %s\n", scanner.Text()) | ||
consoleSender(server, scanner.Text()) | ||
} | ||
}() | ||
|
||
err = cmd.Start() | ||
if err != nil { | ||
fmt.Fprintln(os.Stderr, "Error starting Cmd", err) | ||
return | ||
} | ||
|
||
err = cmd.Wait() | ||
if err != nil { | ||
fmt.Fprintln(os.Stderr, "Error waiting for Cmd", err) | ||
return | ||
} | ||
|
||
} | ||
|
||
func composeUpCoreDOD() { | ||
|
||
home, _ := os.UserHomeDir() | ||
color.Yellow.Println("Running on", runtime.GOOS) | ||
switch runtime.GOOS { | ||
case "linux", "darwin": | ||
// cmd := exec.Command("docker-compose", "-f", composefile, "up", "-d") | ||
cmd := exec.Command("docker", "run", "--rm", "-v", "/var/run/docker.sock:/var/run/docker.sock", "-v", home+"/.kube/config:/root/.kube/config:ro", "-v", home+"/.minikube:/home/$USER/.minikube:ro", "--mount=type=volume,src=instant,dst=/instant", "--network", "host", "openhie/instant:latest", "init", "-t", "docker") | ||
|
||
var outb, errb bytes.Buffer | ||
cmd.Stdout = &outb | ||
cmd.Stderr = &errb | ||
// cmd.Stdout = os.Stdout | ||
// cmd.Stderr = os.Stderr | ||
err := cmd.Run() | ||
if err != nil { | ||
log.Fatalf("cmd.Run() failed with %s\n", err) | ||
|
||
} | ||
consoleSender(server, outb.String()) | ||
fmt.Println("out:", outb.String(), "err:", errb.String()) | ||
|
||
case "windows": | ||
// cmd := exec.Command("cmd", "/C", "docker-compose", "-f", composefile, "up", "-d") | ||
cmd := exec.Command("cmd", "/C", "docker", "run", "--rm", "-v", "/var/run/docker.sock:/var/run/docker.sock", "-v", home+"\\.kube:/root/.kube/config:ro", "--mount=type=volume,src=instant,dst=/instant", "openhie/instant:latest", "init", "-t", "docker") | ||
cmd.Stdout = os.Stdout | ||
cmd.Stderr = os.Stderr | ||
if err := cmd.Run(); err != nil { | ||
fmt.Println("Error: ", err) | ||
} | ||
default: | ||
consoleSender(server, "What operating system is this?") | ||
} | ||
|
||
} |
Oops, something went wrong.