Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Dockerfile.goreleaser
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ ARG TARGETPLATFORM
ARG BINARY_NAME

# Install certificates and timezone data
RUN apk add --no-cache ca-certificates tzdata curl
RUN apk add --no-cache ca-certificates tzdata

# Set the Current Working Directory inside the container
WORKDIR /app
Expand All @@ -21,7 +21,7 @@ USER guest
ENTRYPOINT ["/docker-entrypoint.sh"]

HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
CMD curl -fs http://localhost:3000/health/live
CMD [ "/app/app", "health" ]

CMD [ "/app/app" ]

5 changes: 2 additions & 3 deletions build/package/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ FROM alpine:3 AS prod

WORKDIR /app

RUN apk add --no-cache tzdata \
curl
RUN apk add --no-cache tzdata

COPY scripts/docker-entrypoint.sh /docker-entrypoint.sh

Expand All @@ -48,6 +47,6 @@ USER guest
ENTRYPOINT ["/docker-entrypoint.sh"]

HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
CMD curl -fs http://localhost:3000/health/live
CMD [ "/app/app", "health" ]

CMD [ "/app/app" ]
17 changes: 12 additions & 5 deletions cmd/sms-gateway/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ package main
import (
"os"

"github.com/android-sms-gateway/server/internal/health"
smsgateway "github.com/android-sms-gateway/server/internal/sms-gateway"
"github.com/android-sms-gateway/server/internal/worker"
)

const (
cmdWorker = "worker"
cmdHealth = "health"
)

// @securitydefinitions.basic ApiAuth
Expand Down Expand Up @@ -53,13 +55,18 @@ const (
func main() {
args := os.Args[1:]
cmd := "start"
if len(args) > 0 && args[0] == cmdWorker {
cmd = cmdWorker
if len(args) > 0 {
cmd = args[0]
}

if cmd == cmdWorker {
switch cmd {
case cmdHealth:
health.Run()
return
case cmdWorker:
worker.Run()
} else {
smsgateway.Run()
return
}

smsgateway.Run()
}
37 changes: 37 additions & 0 deletions internal/health/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package health

import (
"context"
"time"

"github.com/android-sms-gateway/server/internal/config"
"github.com/go-core-fx/logger"
"go.uber.org/fx"
)

func Run() {
fx.New(
fx.StartTimeout(time.Second),
logger.Module(),
logger.WithFxDefaultLogger(),
config.Module(),
module(),
).Run()
}

func module() fx.Option {
return fx.Module(
"health",
fx.Provide(NewChecker),
fx.Invoke(func(lc fx.Lifecycle, checker *Checker) {
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
return checker.Execute(ctx)
},
OnStop: func(_ context.Context) error {
return nil
},
})
}),
)
}
74 changes: 74 additions & 0 deletions internal/health/checker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package health

import (
"context"
"errors"
"fmt"
"io"
httpclient "net/http"
"time"

"github.com/capcom6/go-infra-fx/http"
"go.uber.org/fx"
"go.uber.org/zap"
)

var ErrNotHealthy = errors.New("not healthy")

type Checker struct {
config http.Config

shutdowner fx.Shutdowner
logger *zap.Logger
}

func NewChecker(config http.Config, shutdowner fx.Shutdowner, logger *zap.Logger) *Checker {
return &Checker{
config: config,
shutdowner: shutdowner,
logger: logger,
}
}

func (c *Checker) Execute(ctx context.Context) error {
ctx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()

client := httpclient.DefaultClient

req, err := httpclient.NewRequestWithContext(
ctx,
httpclient.MethodGet,
"http://"+c.config.Listen+"/health/live",
nil,
)
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}

res, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to send request: %w", err)
}
defer res.Body.Close()

body, err := io.ReadAll(res.Body)
if err != nil {
return fmt.Errorf("failed to read response body: %w", err)
}

c.logger.Info(string(body))

if res.StatusCode >= httpclient.StatusBadRequest {
c.logger.Error("health check failed", zap.Int("status", res.StatusCode), zap.String("body", string(body)))
return fmt.Errorf("%w: health check failed: %s", ErrNotHealthy, string(body))
}

c.logger.Info("health check passed", zap.Int("status", res.StatusCode))

if shErr := c.shutdowner.Shutdown(); shErr != nil {
c.logger.Error("failed to shutdown", zap.Error(shErr))
}

return nil
}