Skip to content

Commit c824883

Browse files
authored
A new SSE server for ETOS API (#44)
* Make a new SSE server in Golang
1 parent cbef8d8 commit c824883

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+1757
-2
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ jobs:
2727
run: pip install -U setuptools
2828
- name: Run Tox
2929
run: tox
30+
working-directory: ./python
3031

3132
run-hadolint:
3233
# The type of runner that the job will run on

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ coverage.xml
4040

4141
# Build and docs folder/files
4242
build/*
43+
bin/*
4344
dist/*
4445
sdist/*
4546
docs/api/*

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
FROM python:3.9.0-buster AS build
22

33
COPY . /src
4-
WORKDIR /src
4+
WORKDIR /src/python
55
RUN python3 setup.py bdist_wheel
66

77
FROM python:3.9.0-slim-buster
88
ARG TZ
99
ENV TZ=$TZ
1010

11-
COPY --from=build /src/dist/*.whl /tmp
11+
COPY --from=build /src/python/dist/*.whl /tmp
1212

1313
# hadolint ignore=DL3013
1414
# hadolint ignore=DL3008

Makefile

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Install tools locally instead of in $HOME/go/bin.
2+
export GOBIN := $(CURDIR)/bin
3+
export PATH := $(GOBIN):$(PATH)
4+
5+
export RELEASE_VERSION ?= $(shell git describe --always)
6+
export DOCKER_REGISTRY ?= registry.nordix.org
7+
export DOCKER_NAMESPACE ?= eiffel
8+
export DEPLOY ?= etos-sse
9+
10+
COMPILEDAEMON = $(GOBIN)/CompileDaemon
11+
GIT = git
12+
SSE = $(GOBIN)/etos-sse
13+
GOLANGCI_LINT = $(GOBIN)/golangci-lint
14+
15+
GOLANGCI_LINT_VERSION = v1.52.2
16+
17+
.PHONY: all
18+
all: test build start
19+
20+
.PHONY: build
21+
build:
22+
go build -ldflags="-w -s -buildid=" -trimpath -o $(SSE) ./cmd/sse
23+
24+
.PHONY: clean
25+
clean:
26+
$(RM) $(SSE)
27+
docker compose --project-directory . -f deploy/$(DEPLOY)/docker-compose.yml rm || true
28+
29+
.PHONY: check
30+
check: staticcheck test
31+
32+
.PHONY: staticcheck
33+
staticcheck: $(GOLANGCI_LINT)
34+
$(GOLANGCI_LINT) run
35+
36+
.PHONY: test
37+
test:
38+
go test -cover -timeout 30s -race $(shell go list ./... | grep -v "etos-api/test")
39+
40+
# Start a development docker with a database that restarts on file changes.
41+
.PHONY: start
42+
start: $(COMPILEDAEMON)
43+
docker compose --project-directory . -f deploy/$(DEPLOY)/docker-compose.yml up
44+
45+
.PHONY: stop
46+
stop:
47+
docker compose --project-directory . -f deploy/$(DEPLOY)/docker-compose.yml down
48+
49+
# Build a docker using the production Dockerfiler
50+
.PHONY: docker
51+
# Including the parameter name!
52+
EXTRA_DOCKER_ARGS=
53+
export EXTRA_DOCKER_ARGS
54+
docker:
55+
docker build $(EXTRA_DOCKER_ARGS) -t $(DOCKER_REGISTRY)/$(DOCKER_NAMESPACE)/$(DEPLOY):$(RELEASE_VERSION) -f ./deploy/$(DEPLOY)/Dockerfile .
56+
57+
.PHONY: push
58+
push:
59+
docker push $(DOCKER_REGISTRY)/$(DOCKER_NAMESPACE)/$(DEPLOY):$(RELEASE_VERSION)
60+
61+
.PHONY: tidy
62+
tidy:
63+
go mod tidy
64+
65+
.PHONY: check-dirty
66+
check-dirty:
67+
$(GIT) diff --exit-code HEAD
68+
69+
# Build dependencies
70+
71+
$(COMPILEDAEMON):
72+
mkdir -p $(dir $@)
73+
go install github.com/githubnemo/CompileDaemon@v1.3.0
74+
75+
$(GOLANGCI_LINT):
76+
mkdir -p $(dir $@)
77+
curl -sfL \
78+
https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \
79+
| sh -s -- -b $(GOBIN) $(GOLANGCI_LINT_VERSION)

cmd/sse/main.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Copyright Axis Communications AB.
2+
//
3+
// For a full list of individual contributors, please see the commit history.
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
package main
17+
18+
import (
19+
"context"
20+
"net/http"
21+
"os"
22+
"os/signal"
23+
"runtime/debug"
24+
"syscall"
25+
"time"
26+
27+
"github.com/eiffel-community/etos-api/internal/config"
28+
"github.com/eiffel-community/etos-api/internal/logging"
29+
"github.com/eiffel-community/etos-api/internal/server"
30+
"github.com/eiffel-community/etos-api/pkg/application"
31+
v1 "github.com/eiffel-community/etos-api/pkg/sse/v1"
32+
v1alpha "github.com/eiffel-community/etos-api/pkg/sse/v1alpha"
33+
"github.com/sirupsen/logrus"
34+
"github.com/snowzach/rotatefilehook"
35+
"go.elastic.co/ecslogrus"
36+
)
37+
38+
// main sets up logging and starts up the sse webserver.
39+
func main() {
40+
cfg := config.Get()
41+
ctx := context.Background()
42+
43+
var hooks []logrus.Hook
44+
if fileHook := fileLogging(cfg); fileHook != nil {
45+
hooks = append(hooks, fileHook)
46+
}
47+
logger, err := logging.Setup(cfg.LogLevel(), hooks)
48+
if err != nil {
49+
logrus.Fatal(err.Error())
50+
}
51+
52+
hostname, err := os.Hostname()
53+
if err != nil {
54+
logrus.Fatal(err.Error())
55+
}
56+
log := logger.WithFields(logrus.Fields{
57+
"hostname": hostname,
58+
"application": "ETOS API SSE Server",
59+
"version": vcsRevision(),
60+
"name": "ETOS API",
61+
})
62+
63+
log.Info("Loading SSE routes")
64+
v1AlphaSSE := v1alpha.New(cfg, log, ctx)
65+
defer v1AlphaSSE.Close()
66+
v1SSE := v1.New(cfg, log, ctx)
67+
defer v1SSE.Close()
68+
69+
app := application.New(v1AlphaSSE, v1SSE)
70+
srv := server.NewWebService(cfg, log, app)
71+
72+
done := make(chan os.Signal, 1)
73+
signal.Notify(done, syscall.SIGINT, syscall.SIGTERM)
74+
75+
go func() {
76+
if err := srv.Start(); err != nil && err != http.ErrServerClosed {
77+
log.Errorf("Webserver shutdown: %+v", err)
78+
}
79+
}()
80+
81+
sig := <-done
82+
log.Infof("%s received", sig.String())
83+
84+
ctx, cancel := context.WithTimeout(ctx, 1*time.Minute)
85+
defer cancel()
86+
87+
if err := srv.Close(ctx); err != nil {
88+
log.Errorf("Webserver shutdown failed: %+v", err)
89+
}
90+
log.Info("Wait for shutdown to complete")
91+
}
92+
93+
// fileLogging adds a hook into a slice of hooks, if the filepath configuration is set
94+
func fileLogging(cfg config.Config) logrus.Hook {
95+
if filePath := cfg.LogFilePath(); filePath != "" {
96+
// TODO: Make these parameters configurable.
97+
// NewRotateFileHook cannot return an error which is why it's set to '_'.
98+
rotateFileHook, _ := rotatefilehook.NewRotateFileHook(rotatefilehook.RotateFileConfig{
99+
Filename: filePath,
100+
MaxSize: 10, // megabytes
101+
MaxBackups: 3,
102+
MaxAge: 0, // days
103+
Level: logrus.DebugLevel,
104+
Formatter: &ecslogrus.Formatter{
105+
DataKey: "labels",
106+
},
107+
})
108+
return rotateFileHook
109+
}
110+
return nil
111+
}
112+
113+
// vcsRevision returns vcs revision from build info, if any. Otherwise '(unknown)'.
114+
func vcsRevision() string {
115+
buildInfo, ok := debug.ReadBuildInfo()
116+
if !ok {
117+
return "(unknown)"
118+
}
119+
for _, val := range buildInfo.Settings {
120+
if val.Key == "vcs.revision" {
121+
return val.Value
122+
}
123+
}
124+
return "(unknown)"
125+
}

configs/development.env

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
LOGLEVEL=DEBUG
2+
SERVICE_PORT=8080
3+
ETOS_NAMESPACE=default

deploy/etos-sse/Dockerfile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
FROM golang:1.20-alpine AS build
2+
WORKDIR /tmp/sse
3+
COPY . .
4+
RUN apk add --no-cache make=4.4.1-r1 git=2.40.1-r0 && make build
5+
6+
FROM alpine:3.17.3
7+
ARG TZ
8+
ENV TZ=$TZ
9+
10+
LABEL org.opencontainers.image.source=https://github.com/eiffel-community/etos-api
11+
LABEL org.opencontainers.image.authors=etos-maintainers@googlegroups.com
12+
LABEL org.opencontainers.image.licenses=Apache-2.0
13+
14+
RUN apk add --no-cache tzdata=2023c-r0
15+
ENTRYPOINT ["/app/etos-sse"]
16+
17+
COPY --from=build /tmp/sse/bin/etos-sse /app/etos-sse

deploy/etos-sse/Dockerfile.dev

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
FROM golang:1.20
2+
WORKDIR /app
3+
4+
COPY ./go.mod ./go.sum ./
5+
RUN go mod tidy
6+
COPY . .
7+
RUN git config --global --add safe.directory /app
8+
EXPOSE 8080

deploy/etos-sse/docker-compose.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
version: "3.7"
2+
services:
3+
etos-sse:
4+
build:
5+
context: .
6+
dockerfile: ./deploy/etos-sse/Dockerfile.dev
7+
args:
8+
http_proxy: "${http_proxy}"
9+
https_proxy: "${https_proxy}"
10+
volumes:
11+
- ./:/app
12+
ports:
13+
- 8080:8080
14+
env_file:
15+
- ./configs/development.env
16+
entrypoint: ["/bin/bash", "./scripts/entrypoint.sh"]

go.mod

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
module github.com/eiffel-community/etos-api
2+
3+
go 1.20
4+
5+
require (
6+
github.com/julienschmidt/httprouter v1.3.0
7+
github.com/sirupsen/logrus v1.9.3
8+
github.com/snowzach/rotatefilehook v0.0.0-20220211133110-53752135082d
9+
github.com/stretchr/testify v1.8.2
10+
go.elastic.co/ecslogrus v1.0.0
11+
k8s.io/apimachinery v0.28.2
12+
k8s.io/client-go v0.28.2
13+
)
14+
15+
require (
16+
github.com/davecgh/go-spew v1.1.1 // indirect
17+
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
18+
github.com/go-logr/logr v1.2.4 // indirect
19+
github.com/go-openapi/jsonpointer v0.19.6 // indirect
20+
github.com/go-openapi/jsonreference v0.20.2 // indirect
21+
github.com/go-openapi/swag v0.22.3 // indirect
22+
github.com/gogo/protobuf v1.3.2 // indirect
23+
github.com/golang/protobuf v1.5.3 // indirect
24+
github.com/google/gnostic-models v0.6.8 // indirect
25+
github.com/google/go-cmp v0.5.9 // indirect
26+
github.com/google/gofuzz v1.2.0 // indirect
27+
github.com/google/uuid v1.3.0 // indirect
28+
github.com/josharian/intern v1.0.0 // indirect
29+
github.com/json-iterator/go v1.1.12 // indirect
30+
github.com/magefile/mage v1.9.0 // indirect
31+
github.com/mailru/easyjson v0.7.7 // indirect
32+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
33+
github.com/modern-go/reflect2 v1.0.2 // indirect
34+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
35+
github.com/pmezard/go-difflib v1.0.0 // indirect
36+
golang.org/x/net v0.13.0 // indirect
37+
golang.org/x/oauth2 v0.8.0 // indirect
38+
golang.org/x/sys v0.10.0 // indirect
39+
golang.org/x/term v0.10.0 // indirect
40+
golang.org/x/text v0.11.0 // indirect
41+
golang.org/x/time v0.3.0 // indirect
42+
google.golang.org/appengine v1.6.7 // indirect
43+
google.golang.org/protobuf v1.30.0 // indirect
44+
gopkg.in/inf.v0 v0.9.1 // indirect
45+
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
46+
gopkg.in/yaml.v2 v2.4.0 // indirect
47+
gopkg.in/yaml.v3 v3.0.1 // indirect
48+
k8s.io/api v0.28.2 // indirect
49+
k8s.io/klog/v2 v2.100.1 // indirect
50+
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect
51+
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect
52+
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
53+
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
54+
sigs.k8s.io/yaml v1.3.0 // indirect
55+
)

0 commit comments

Comments
 (0)