Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Websocket logs endpoint for polling envoy fleet container logs #80

Merged
merged 19 commits into from
Nov 25, 2022
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
14 changes: 9 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,19 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1

- name: Build and push
- name: Build and push api-server
uses: docker/build-push-action@v2
with:
context: server
file: server/Dockerfile
context: .
file: ./build/api-server/Dockerfile
platforms: linux/amd64,linux/arm64

# - name: build
# run: make build
- name: Build and push websocket
uses: docker/build-push-action@v2
with:
context: .
file: ./build/websocket/Dockerfile
platforms: linux/amd64,linux/arm64

- name: Set up Go
uses: actions/setup-go@v2
Expand Down
18 changes: 15 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,23 @@ jobs:
- name: setup-goreleaser-environment-variables
run: |
echo "VERSION=$(git describe --tags $(git rev-list --tags --max-count=1))" >> $GITHUB_ENV
- name: Build and Push
- name: Build and Push Kusk Gateway API
uses: docker/build-push-action@v2
with:
context: server
file: server/Dockerfile
context: .
file: ./build/api-server/Dockerfile
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
TELEMETRY_TOKEN=${{ secrets.TELEMETRY_TOKEN }}
VERSION=${{ env.VERSION }}
- name: Build and Push Kusk Gateway API Websocket
uses: docker/build-push-action@v2
with:
context: .
file: ./build/websocket/Dockerfile
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
Expand Down
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
.PHONY: all
all: format test build

.PHONY: build
build:
docker build -t kusk-gateway-api server
docker buildx build -t kubeshop/kusk-gateway-api -f build/api-server/Dockerfile .
docker buildx build -t kubeshop/kusk-gateway-api-websocket -f build/websocket/Dockerfile .

server-generate:
openapi-generator-cli generate -i api/openapi.yaml -g go-server -o server/ --additional-properties=featureCORS=true
Expand Down
39 changes: 39 additions & 0 deletions api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,45 @@ paths:
type: object
404:
description: "Envoy fleet CRD not found"
/logs:
x-kusk:
upstream:
service:
name: kusk-gateway-api
namespace: kusk-system
port: 8081
rewrite:
pattern: "^/api"
substitution: ""
websocket: true
get:
tags:
- "fleets"
summary: "Get envoy fleet logs"
operationId: getEnvoyFleetLogs
parameters:
- name: namespace
in: query
required: true
schema:
type: string
default: "kusk-system"
- name: name
in: query
required: true
schema:
type: string
default: "kusk-gateway-envoy-fleet"
responses:
200:
description: "Envoy fleet logs"
content:
application/json:
schema:
type: object
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we change the type to an array of string? because it generates a wrong type

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's it? Are you using a library on the front end to generate stuff?

404:
description: "Envoy fleet logs not found"

/staticroutes:
get:
tags:
Expand Down
6 changes: 3 additions & 3 deletions server/Dockerfile → build/api-server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ FROM --platform=$BUILDPLATFORM docker.io/golang:1.18 as builder
WORKDIR /go/src
# Copy `go.mod` for definitions and `go.sum` to invalidate the next layer
# in case of a change in the dependencies
COPY go.mod go.sum ./
COPY ./server/go.mod ./server/go.sum ./
# Download dependencies
RUN go mod download

Expand All @@ -11,8 +11,8 @@ ARG VERSION
ARG TARGETARCH
ARG TARGETOS

COPY . .
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -v -ldflags "-X github.com/kubeshop/kusk-gateway/pkg/analytics.TelemetryToken=$TELEMETRY_TOKEN -X github.com/kubeshop/kusk-gateway/pkg/build.Version=$VERSION" -o kusk-gateway-api
COPY ./server ./
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -v -ldflags "-X github.com/kubeshop/kusk-gateway/pkg/analytics.TelemetryToken=$TELEMETRY_TOKEN -X github.com/kubeshop/kusk-gateway/pkg/build.Version=$VERSION" -o kusk-gateway-api cmd/api-server/main.go

FROM --platform=$BUILDPLATFORM gcr.io/distroless/static:nonroot
COPY --from=builder --chown=65532:65532 /go/src/kusk-gateway-api ./
Expand Down
22 changes: 22 additions & 0 deletions build/websocket/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM --platform=$BUILDPLATFORM docker.io/golang:1.18 as builder
WORKDIR /go/src
# Copy `go.mod` for definitions and `go.sum` to invalidate the next layer
# in case of a change in the dependencies
COPY ../server/go.mod ../server/go.sum ./
# Download dependencies
RUN go mod download

ARG TELEMETRY_TOKEN
ARG VERSION
ARG TARGETARCH
ARG TARGETOS

COPY ../server ./
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -v -ldflags "-X github.com/kubeshop/kusk-gateway/pkg/analytics.TelemetryToken=$TELEMETRY_TOKEN -X github.com/kubeshop/kusk-gateway/pkg/build.Version=$VERSION" -o kusk-gateway-api-websocket cmd/websocket/*.go

FROM --platform=$BUILDPLATFORM gcr.io/distroless/static:nonroot
COPY --from=builder --chown=65532:65532 /go/src/kusk-gateway-api-websocket ./

USER 65532:65532

ENTRYPOINT ["./kusk-gateway-api-websocket"]
3 changes: 3 additions & 0 deletions docker-compose-minikube.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ services:
kgwapi:
volumes:
- $HOME/.minikube:$HOME/.minikube:ro
websocket:
volumes:
- $HOME/.minikube:$HOME/.minikube:ro
16 changes: 14 additions & 2 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
version: "3"
services:
kgwapi:
build: ./server
build:
context: .
dockerfile: ./build/api-server/Dockerfile
environment:
- KUBECONFIG=/kube/config
- ANALYTICS_ENABLED=false
volumes:
- $HOME/.kube/config:/kube/config:ro
ports:
- 8080:8080
- "8080:8080"
websocket:
build:
context: .
dockerfile: ./build/websocket/Dockerfile
environment:
- KUBECONFIG=/kube/config
volumes:
- $HOME/.kube/config:/kube/config:ro
ports:
- "8081:8080"
16 changes: 13 additions & 3 deletions kgw-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,15 @@ spec:
app: kgwtest
spec:
containers:
- image: jasmingacic/kusk-gateway-api:latest
imagePullPolicy: Always
name: kuskgateway-api
- image: kubeshop/kusk-gateway-api:latest
imagePullPolicy: IfNotPresent
name: kusk-gateway-api
- image: kubeshop/kusk-gateway-api-websocket:latest
imagePullPolicy: IfNotPresent
args:
- --port=8081
name: kusk-gateway-api-websocket

serviceAccountName: kusk-gateway-manager
---
apiVersion: v1
Expand All @@ -32,7 +38,11 @@ metadata:
spec:
ports:
- port: 8080
name: http
targetPort: 8080
- port: 8081
name: websocket
targetPort: 8081
type: LoadBalancer
selector:
app: kgwtest
12 changes: 9 additions & 3 deletions server/main.go → server/cmd/api-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,20 @@ func getConfig() (*rest.Config, error) {

return config, err
}

func getClient() (client.Client, error) {
scheme := runtime.NewScheme()
kuskv1.AddToScheme(scheme)
corev1.AddToScheme(scheme)
if err := kuskv1.AddToScheme(scheme); err != nil {
return nil, fmt.Errorf("unable to add kusk scheme: %w", err)
}

if err := corev1.AddToScheme(scheme); err != nil {
return nil, fmt.Errorf("unable to add core scheme: %w", err)
}

config, err := getConfig()
if err != nil {
return nil, err
return nil, fmt.Errorf("unable to get kubernetes config: %w", err)
}

return client.New(config, client.Options{Scheme: scheme})
Expand Down
99 changes: 99 additions & 0 deletions server/cmd/websocket/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package main

import (
"bufio"
"context"
"io"
"log"
"time"

"github.com/gorilla/websocket"
)

type client struct {
conn *websocket.Conn
logStream io.ReadCloser

writeWait time.Duration
pongWait time.Duration
pingPeriod time.Duration
maxMessageSize int64
}

func (c *client) readPump(ctx context.Context, stopCh chan struct{}) {
defer func() {
c.conn.Close()
stopCh <- struct{}{}
}()
c.conn.SetReadLimit(c.maxMessageSize)
c.conn.SetReadDeadline(time.Now().Add(c.pongWait))
c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(c.pongWait)); return nil })
for {
select {
case <-ctx.Done():
return
default:
_, _, err := c.conn.ReadMessage()
if err != nil {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure, websocket.CloseNormalClosure) {
log.Printf("error: %v", err)
}
return
}
}
}
}

func (c *client) writePump(ctx context.Context, stopCh chan struct{}) {
ticker := time.NewTicker(c.pingPeriod)
defer func() {
ticker.Stop()
c.conn.Close()
stopCh <- struct{}{}
}()

reader := bufio.NewReader(c.logStream)
for {
select {
case <-ctx.Done():
case <-stopCh:
return
case <-ticker.C:
c.conn.SetWriteDeadline(time.Now().Add(c.writeWait))
if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
return
}
default:
c.conn.SetWriteDeadline(time.Now().Add(c.writeWait))
line, err := readLongLine(reader)
if err != nil {
log.Println("writePump: cannot read line", err)
continue
}

if err := c.conn.WriteMessage(websocket.TextMessage, line); err != nil {
log.Println("writePump: cannot write message", err)
continue
}
}
}
}

func readLongLine(r *bufio.Reader) (line []byte, err error) {
var buffer []byte
var isPrefix bool

for {
buffer, isPrefix, err = r.ReadLine()
line = append(line, buffer...)
if err != nil {
break
}

if !isPrefix {
break
}
}

return line, err
}
Loading