Skip to content

Commit

Permalink
Added build option before running a local application
Browse files Browse the repository at this point in the history
  • Loading branch information
eko committed Oct 29, 2020
1 parent 7772de4 commit 76bed57
Show file tree
Hide file tree
Showing 15 changed files with 442 additions and 45 deletions.
1 change: 1 addition & 0 deletions Makefile
Expand Up @@ -16,6 +16,7 @@ mocks: ## Generate mocks for tests
@echo "> generating mocks..."

# Monday
mockgen -source=pkg/build/builder.go -destination=pkg/build/builder_mock.go -package=build
mockgen -source=pkg/ui/view.go -destination=pkg/ui/view_mock.go -package=ui
mockgen -source=pkg/hostfile/client.go -destination=pkg/hostfile/client_mock.go -package=hostfile
mockgen -source=pkg/proxy/proxy.go -destination=pkg/proxy/proxy_mock.go -package=proxy
Expand Down
64 changes: 55 additions & 9 deletions README.md
Expand Up @@ -61,12 +61,6 @@ $ go get -u github.com/eko/monday
$ git clone https://github.com/eko/monday.git
```

Install the needed vendors:

```
$ GO111MODULE=on go mod vendor
```

Then, build the binary using the available target in Makefile:
```bash
$ make build
Expand All @@ -83,11 +77,63 @@ Please note that you can also split this configuration in multiple files by resp
* `~/monday.forwards.yaml`
* `~/monday.projects.yaml`

This will help you in having smaller and more readable configuration files.
This will help you navigate more easily in your configuration files.

### Define a local project

Here is an example of a local application:

```yaml
<: &graphql-local
name: graphql
path: $GOPATH/src/github.com/eko/graphql
watch: true
hostname: graphql.svc.local # Project will be available using this hostname on your machine
setup: # Setup, installation step in case specified path does not exists
- go get github.com/eko/graphql
build: # Optionally, you can define a build section to build your application before running it
type: command
commands:
- go build -o ./build/graphql-app cmd/ # Here, just build the Go application
env:
CGO_ENABLED: on
executable: ./build/graphql-app # Then, run it using the executable
env: # Optional, in case you want to specify some environment variables for this app
HTTP_PORT: 8005
env_file: "github.com/eko/graphql/.env" # Or via a .env file also
```

To learn more about the configuration, please take a look at the [Configuration Wiki page](https://github.com/eko/monday/wiki/Configuration).
Then, imagine this GraphQL instance needs to call a user-api but we want to forward it from a Kubernetes environment, we will define it as:

```yaml
<: &user-api-forward
name: user-api
type: kubernetes
values:
context: staging # This is your kubernetes cluster (kubectl config context name)
namespace: backend
labels:
app: user-api
hostname: user-api.svc.local # API will be available under this hostname
ports:
- 8080:8080
```

For an overview of what's possible with configuration file, please look at the [configuration example file here](https://raw.githubusercontent.com/eko/monday/master/example.yaml).
Well, you have defined both a local app and an application that needs to be forwarded, now just create the project with:

```yaml
- name: graphql
local:
- *graphql-local
forward:
- *user-api-forward
```

Your project configuration is ready, you can now work easily with your microservices.

For an overview of what's possible to do with configuration file, please look at the [configuration example file here](https://raw.githubusercontent.com/eko/monday/master/example.yaml).

To learn more about the configuration, please take a look at the [Configuration Wiki page](https://github.com/eko/monday/wiki/Configuration).

## Usage: Run your projects!
[![Monday Asciinema](https://asciinema.org/a/aB9ZkCmJS6m1b4uv8Dio1i59U.svg)](https://asciinema.org/a/aB9ZkCmJS6m1b4uv8Dio1i59U)
Expand Down
5 changes: 4 additions & 1 deletion cmd/main.go
Expand Up @@ -8,6 +8,7 @@ import (
"strings"

"github.com/eko/monday/internal/runtime"
"github.com/eko/monday/pkg/build"
"github.com/eko/monday/pkg/config"
"github.com/eko/monday/pkg/forward"
"github.com/eko/monday/pkg/hostfile"
Expand All @@ -30,6 +31,7 @@ var (

proxyfier proxy.Proxy
forwarder forward.Forwarder
builder build.Builder
runner run.Runner
watcher watch.Watcher

Expand Down Expand Up @@ -121,10 +123,11 @@ func runProject(conf *config.Config, choice string) {
}

proxyfier = proxy.NewProxy(layout.GetProxyView(), hostfile)
builder = build.NewBuilder(layout.GetLogsView(), project)
runner = run.NewRunner(layout.GetLogsView(), proxyfier, project)
forwarder = forward.NewForwarder(layout.GetForwardsView(), proxyfier, project)

watcher = watch.NewWatcher(runner, forwarder, conf.Watcher, project)
watcher = watch.NewWatcher(builder, runner, forwarder, conf.Watcher, project)
go watcher.Watch()

if uiEnabled {
Expand Down
17 changes: 15 additions & 2 deletions example.yaml
Expand Up @@ -16,13 +16,26 @@ watcher: # Optional
path: github.com/eko/graphql # Will find in GOPATH (as executable is "go")
watch: true # Default: false (do not watch directory)
hostname: graphql.svc.local # Optional, in case you want to map a specific hostname with a single IP address
executable: go
build: # Optionally, you can define a build section to build your application before running it
type: command # There is only a 'command' type actually but maybe we could add more in the future
commands:
- docker build -t graphql-image .
env:
DOCKER_BUILDKIT: 1
executable: docker
args:
- run
- cmd/main.go
- --rm
- --name
- graphql
- graphql-image
env: # Optional, in case you want to specify some environment variables for this app
HTTP_PORT: 8005
env_file: "github.com/eko/graphql/.env" # Optional, in case you want to specify some environment variables from a file
stop_executable: docker
stop_args:
- stop
- graphql
setup: # Optional, in case you want to setup the project first if directory does not exists
- go get github.com/eko/graphql
- echo You can use ~/path syntax and environment variables like $GOPATH in your commands
Expand Down
77 changes: 77 additions & 0 deletions pkg/build/builder.go
@@ -0,0 +1,77 @@
package build

import (
"sync"

"github.com/eko/monday/pkg/build/command"
"github.com/eko/monday/pkg/config"
"github.com/eko/monday/pkg/helper"
"github.com/eko/monday/pkg/ui"
)

// Builder represents a local application builder
type Builder interface {
BuildAll()
Build(application *config.Application)
}

type builder struct {
projectName string
applications []*config.Application
view ui.View
}

// NewBuilder instanciates a new builder instance
func NewBuilder(view ui.View, project *config.Project) *builder {
return &builder{
projectName: project.Name,
applications: project.Applications,
view: view,
}
}

// BuildAll builds all local applications in separated goroutines
func (b *builder) BuildAll() {
var wg = sync.WaitGroup{}

for _, application := range b.applications {
wg.Add(1)
go func(application *config.Application) {
defer wg.Done()
b.Build(application)
}(application)
}

wg.Wait()
}

// Build builds the application
func (b *builder) Build(application *config.Application) {
if application.Build == nil {
return
}

if err := helper.CheckPathExists(application.GetPath()); err != nil {
b.view.Writef("❌ %s\n", err.Error())
return
}

var build = application.Build
var err error

switch build.Type {
case command.BuilderType:
b.view.Writef("⚙️ Building local app '%s' via %s...\n", application.Name, build.Type)
err = command.Build(application, b.view)

default:
b.view.Writef("❌ Unknown build type '%s' for application '%s'\n", build.Type, application.Name)
}

if err != nil {
b.view.Writef("❌ Error while building application '%s': %v\n", application.Name, err)
return
}

b.view.Writef("✅ Build of application '%s' complete!\n", application.Name)
}
59 changes: 59 additions & 0 deletions pkg/build/builder_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

72 changes: 72 additions & 0 deletions pkg/build/builder_test.go
@@ -0,0 +1,72 @@
package build

import (
"testing"

"github.com/eko/monday/pkg/config"
"github.com/eko/monday/pkg/log"
"github.com/eko/monday/pkg/ui"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
)

func TestNewBuilder(t *testing.T) {
// Given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

view := ui.NewMockView(ctrl)

project := getMockedProjectWithApplication()

// When
b := NewBuilder(view, project)

// Then
assert.IsType(t, new(builder), b)
assert.Implements(t, new(Builder), b)

assert.Equal(t, view, b.view)
assert.Equal(t, project.Name, b.projectName)
assert.Equal(t, project.Applications, b.applications)
}

func TestBuildAll(t *testing.T) {
// Given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

view := ui.NewMockView(ctrl)
view.EXPECT().Writef("⚙️ Building local app '%s' via %s...\n", "test-app", "command")
view.EXPECT().Writef("👉 Running commands:\n%s\n", "echo 'ok it works'\necho yes it's ok")
view.EXPECT().Write(log.ColorGreen + "test-app" + log.ColorWhite + " 'ok it works'\n")
view.EXPECT().Write(log.ColorGreen + "test-app" + log.ColorWhite + " yes it's ok\n")
view.EXPECT().Writef("✅ Build of application '%s' complete!\n", "test-app")

project := getMockedProjectWithApplication()

builder := NewBuilder(view, project)

// When - Then
builder.BuildAll()
}

func getMockedProjectWithApplication() *config.Project {
return &config.Project{
Name: "My project name",
Applications: []*config.Application{
{
Name: "test-app",
Path: "/",

Build: &config.Build{
Type: "command",
Commands: []string{
"echo 'ok it works'",
"echo yes it's ok",
},
},
},
},
}
}

0 comments on commit 76bed57

Please sign in to comment.