Skip to content

Commit

Permalink
Merge branch 'main' of github.com:eaceto/ReGraphQL
Browse files Browse the repository at this point in the history
  • Loading branch information
eaceto committed Apr 6, 2022
2 parents 2d16c9f + 0fc992e commit 0bc8849
Show file tree
Hide file tree
Showing 25 changed files with 711 additions and 188 deletions.
17 changes: 15 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ FROM golang:1.18 AS build-env

WORKDIR /go/src/app

COPY app ./app
COPY helpers ./helpers
COPY go.mod .
COPY main.go .
COPY app ./app
COPY helpers ./helpers
COPY services ./services
COPY middlewares ./middlewares

ENV CGO_ENABLED=0
ENV GO111MODULE=on
Expand All @@ -20,8 +22,19 @@ RUN go build -o /go/bin/app
# We don't use /base because we don't need OpenSSL, libSSL and glibc
FROM gcr.io/distroless/static

LABEL org.opencontainers.image.title="ReGraphQL"
LABEL org.opencontainers.image.description="A simple (yet effective) REST / HTTP to GraphQL router"
LABEL org.opencontainers.image.authors="ezequiel.aceto+regraphql@gmail.com"
LABEL org.opencontainers.image.url="https://hub.docker.com/repository/docker/eaceto/regraphql"
LABEL org.opencontainers.image.source="https://github.com/eaceto/ReGraphQL"

LABEL org.opencontainers.image.version="1.0.1"

COPY --from=build-env /go/bin/app /

EXPOSE 8080

HEALTHCHECK CMD curl --fail http://localhost:8080/health/liveness || exit 1

USER 1000
CMD ["/app"]
59 changes: 57 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ GET /persons/{person}
# Index
* [Requirements](#requirements)
* [Features](#features)
* [Service Endpoints](#service-endpoints)
* [Quick start](#quick-start)
* [Docker Image](#docker-image)
* [Contributing](#contributing)
Expand All @@ -76,10 +77,64 @@ GET /persons/{person}
- [x] Reads configuration from **environment variables**
- [x] Logs using Kubernetes' [**klog**](https://github.com/kubernetes/klog) v2
- [x] Docker Image below 20MB
- [ ] Exposes metrics using [Prometheus](https://prometheus.io/)
- [ ] Exposes Liveness, Readiness and Startup Probes
- [X] Exposes metrics using [Prometheus](https://prometheus.io/)
- [X] Exposes Liveness, Readiness and Startup Probes
- [ ] Implements Hot reload for routes

## Service Endpoints

### Liveness

````http request
GET /health/liveness
{"hostname":"pod_67804","status":"up"}
````

Returns HTTP Status Code **OK** (200) with the following JSON as soon as the application starts
````json
{"hostname":"<< hostname >>","status":"up"}
````

### Readiness

````http request
GET /health/readiness
{"hostname":"pod_67804","status":"ready"}
````

1. If the application is **Ready** to receive requests

Returns HTTP Status Code **OK** (200) with the following JSON:
````json
{"hostname":"<< hostname >>","status":"ready"}
````

2. If the application is **Not Ready** to receive requests

Returns HTTP Status Code **Precondition Failed** (412) with the following JSON:
````json
{"hostname":"<< hostname >>","status":"waiting"}
````

### Metrics

The service exposes a [Prometheus](https://prometheus.io/) metrics endpoint at **/metrics**

````http request
GET /metrics
# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 0
go_gc_duration_seconds{quantile="0.25"} 0
go_gc_duration_seconds{quantile="0.5"} 0
go_gc_duration_seconds{quantile="0.75"} 0
go_gc_duration_seconds{quantile="1"} 0
go_gc_duration_seconds_sum 0
````

## Quick start

1. Describe a route in a file using **yaml**, which matches your HTTP endpoint with your GraphQL endpoint and Query
Expand Down
49 changes: 49 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* ReGraphQL - Proxy
* This is the proxy service of project ReGraphQL
*
* Contact: ezequiel.aceto+regraphql@gmail.com
*/

package app

import (
"fmt"
"github.com/gorilla/mux"
)

type Application struct {
Router *mux.Router
Routes []Route
Configuration *Configuration
}

func NewApplication(rootRouter *mux.Router) (*Application, error) {

configuration, err := NewConfiguration()
if err != nil {
return nil, err
}

configuration.log()

routes, err := configuration.loadRoutesFromFiles()
if err != nil {
return nil, err
}

if len(routes) == 0 {
return nil, fmt.Errorf("no routes available in config path: %s", configuration.RouterConfigsPath)
}

router, err := configuration.addServiceHTTPRouter(rootRouter, routes)
if router == nil {
return nil, fmt.Errorf("could not create HTTP router")
}

return &Application{
Router: router,
Routes: routes,
Configuration: configuration,
}, nil
}
41 changes: 34 additions & 7 deletions app/configuration.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
// Copyright 2021 Ezequiel (Kimi) Aceto. All rights reserved.
/*
* ReGraphQL - Proxy
* This is the proxy service of project ReGraphQL
*
* Contact: ezequiel.aceto+regraphql@gmail.com
*/

package app

import (
"fmt"
"github.com/eaceto/ReGraphQL/helpers"
"github.com/spf13/viper"
"k8s.io/klog/v2"
"net"
"net/http"
"os"
Expand All @@ -14,7 +20,7 @@ import (
"time"
)

type App struct {
type Configuration struct {
ServerAddr string
ServicePath string
ServerReadTimeout time.Duration
Expand All @@ -25,7 +31,7 @@ type App struct {
HTTPClient *http.Client
}

func NewApp() (*App, error) {
func NewConfiguration() (*Configuration, error) {

viper.SetConfigFile(EnvironmentVariablesFile)
_ = viper.ReadInConfig()
Expand All @@ -49,7 +55,14 @@ func NewApp() (*App, error) {
servicePath = "/" + servicePath
}

// Parse Server configuration
if servicePath == HealthPath {
return nil, fmt.Errorf("invalid %s value: '%v' has conflicts with reserverd path: %v", ServicePathKey, servicePath, HealthPath)
}
if servicePath == MetricsPath {
return nil, fmt.Errorf("invalid %s value: '%v' has conflicts with reserverd path: %v", ServicePathKey, servicePath, HealthPath)
}

// Parse Server Configuration
serverReadTimeout, serverReadTimeoutError := strconv.Atoi(helpers.GetEnvVar(ServerReadTimeoutKey, ServerTimeoutDefaultValue))
if serverReadTimeoutError != nil || serverReadTimeout < 1 {
return nil, fmt.Errorf("invalid %s value: '%v'. %v", ServerReadTimeoutKey, serverReadTimeout, serverReadTimeoutError)
Expand All @@ -63,14 +76,14 @@ func NewApp() (*App, error) {
traceCallsEnabled := helpers.GetEnvVar(TraceCallsKey, TraceCallsDefaultValue) == "1"
debugEnabled := helpers.GetEnvVar(DebugKey, DebugDefaultValue) == "1"

// Parse path for the router configuration files
// Parse path for the router Configuration files
routerConfigPath := helpers.GetEnvVar(RouterConfigPathKey, RouterConfigPathDefaultValue)
if _, err := os.Stat(routerConfigPath); os.IsNotExist(err) {
return nil, fmt.Errorf("path not found: invalid %s value: '%v'", RouterConfigPathKey, routerConfigPath)
}

// Return application configuration
return &App{
// Return application Configuration
return &Configuration{
ServerAddr: serverHost + ":" + serverPort,
ServicePath: servicePath,
RouterConfigsPath: routerConfigPath,
Expand All @@ -81,3 +94,17 @@ func NewApp() (*App, error) {
HTTPClient: &http.Client{Timeout: time.Duration(serverReadTimeout) * time.Second},
}, nil
}

func (c *Configuration) log() {
if !c.DebugEnabled {
return
}
klog.Warningln("Debug Enabled")

klog.Infof("Config files: %s\n", c.RouterConfigsPath)
klog.Infof("Service path: %s\n", c.ServicePath)

if c.TraceCallsEnabled {
klog.Infoln("TraceCalls Enabled")
}
}
12 changes: 10 additions & 2 deletions app/configuration_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
// Copyright 2021 Ezequiel (Kimi) Aceto. All rights reserved.
/*
* ReGraphQL - Proxy
* This is the proxy service of project ReGraphQL
*
* Contact: ezequiel.aceto+regraphql@gmail.com
*/

package app

import (
"fmt"
"github.com/gorilla/mux"
"strconv"
"testing"
"time"
Expand All @@ -12,13 +18,15 @@ import (
func TestDefaultConfig(t *testing.T) {
testConfigPath := "../tests/files/starwars/"
t.Setenv(RouterConfigPathKey, testConfigPath) // Avoid an error as config directory does not exists
config, err := NewApp()
application, err := NewApplication(mux.NewRouter())

if err != nil {
t.Error(err)
return
}

config := application.Configuration

if config.DebugEnabled != false {
t.Errorf("Debug should be disabled by default ")
}
Expand Down
10 changes: 9 additions & 1 deletion app/constants.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
// Copyright 2021 Ezequiel (Kimi) Aceto. All rights reserved.
/*
* ReGraphQL - Proxy
* This is the proxy service of project ReGraphQL
*
* Contact: ezequiel.aceto+regraphql@gmail.com
*/

package app

Expand Down Expand Up @@ -28,4 +33,7 @@ const (
DebugDefaultValue = "0"

PreAllocatedRoutesNumber = 10

HealthPath = "/health"
MetricsPath = "/metrics"
)
7 changes: 6 additions & 1 deletion app/helpers.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
// Copyright 2021 Ezequiel (Kimi) Aceto. All rights reserved.
/*
* ReGraphQL - Proxy
* This is the proxy service of project ReGraphQL
*
* Contact: ezequiel.aceto+regraphql@gmail.com
*/

package app

Expand Down
Loading

0 comments on commit 0bc8849

Please sign in to comment.