Skip to content

Commit

Permalink
feat: Support multiple renderer render in skaffold dev and skaffold r…
Browse files Browse the repository at this point in the history
…un (#7697)

* feat: allow multiple mixed in renderers

* add integration tests and fix examples

* fix example

* fix integration tests

* fix lint

* fix lint
  • Loading branch information
tejal29 authored Aug 1, 2022
1 parent 07878a3 commit e7c05f5
Show file tree
Hide file tree
Showing 24 changed files with 985 additions and 7 deletions.
4 changes: 4 additions & 0 deletions integration/examples/multiple-renderers/backend/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
apiVersion: v1
description: back end for guestbook
name: backend
version: 0.1.0
29 changes: 29 additions & 0 deletions integration/examples/multiple-renderers/backend/src/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Use base golang image from Docker Hub
FROM golang:1.17 as build

WORKDIR /app

# Copy the go.mod and go.sum, download the dependencies
COPY go.mod go.sum ./
RUN go mod download

# Copy rest of the application source code
COPY . ./

# Compile the application to /app/backend.
# Skaffold passes in debug-oriented compiler flags
ARG SKAFFOLD_GO_GCFLAGS
RUN echo "Go gcflags: ${SKAFFOLD_GO_GCFLAGS}"
RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -mod=readonly -v -o /app/backend .

# Now create separate deployment image
FROM gcr.io/distroless/base

# Definition of this variable is used by 'skaffold debug' to identify a golang binary.
# Default behavior - a failure prints a stack trace for the current goroutine.
# See https://golang.org/pkg/runtime/
ENV GOTRACEBACK=single

WORKDIR /app
COPY --from=build /app/backend /app/backend
ENTRYPOINT ["/app/backend"]
10 changes: 10 additions & 0 deletions integration/examples/multiple-renderers/backend/src/database.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package main

import (
"context"
)

type database interface {
entries(context.Context) ([]guestbookEntry, error)
addEntry(context.Context, guestbookEntry) error
}
9 changes: 9 additions & 0 deletions integration/examples/multiple-renderers/backend/src/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module backend

go 1.12

require (
github.com/DataDog/zstd v1.4.4 // indirect
github.com/xdg/stringprep v1.0.0 // indirect
go.mongodb.org/mongo-driver v1.8.4
)
203 changes: 203 additions & 0 deletions integration/examples/multiple-renderers/backend/src/go.sum

Large diffs are not rendered by default.

117 changes: 117 additions & 0 deletions integration/examples/multiple-renderers/backend/src/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package main

import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"time"

"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
)

// guestbookEntry represents the message object returned in the API.
type guestbookEntry struct {
Author string `json:"author" bson:"author"`
Message string `json:"message" bson:"message"`
Date time.Time `json:"date" bson:"date"`
}

type guestbookServer struct {
db database
}

// main starts a server listening on $PORT responding to requests "GET
// /messages" and "POST /messages" with a JSON API.
func main() {
ctx := context.Background()

// PORT environment variable is set in guestbook-backend.deployment.yaml.
port := os.Getenv("PORT")
if port == "" {
log.Fatal("PORT environment variable not specified")
}
// GUESTBOOK_DB_ADDR environment variable is set in guestbook-backend.deployment.yaml.
dbAddr := os.Getenv("GUESTBOOK_DB_ADDR")
if dbAddr == "" {
log.Fatal("GUESTBOOK_DB_ADDR environment variable not specified")
}

mongoURI := "mongodb://" + dbAddr
connCtx, cancel := context.WithTimeout(ctx, time.Second*30)
defer cancel()
dbConn, err := mongo.Connect(connCtx, options.Client().ApplyURI(mongoURI))
if err != nil {
log.Fatalf("failed to initialize connection to mongodb: %+v", err)
}
if err := dbConn.Ping(connCtx, readpref.Primary()); err != nil {
log.Fatalf("ping to mongodb failed: %+v", err)
}

gs := &guestbookServer{
db: &mongodb{
conn: dbConn,
},
}

log.Printf("backend server listening on port %s", port)
http.Handle("/messages", gs)
if err := http.ListenAndServe(":"+port, nil); err != nil {
log.Fatal(err)
}
}

func (s *guestbookServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Printf("received request: method=%s path=%s", r.Method, r.URL.Path)
if r.Method == http.MethodGet {
s.getMessagesHandler(w, r)
} else if r.Method == http.MethodPost {
s.postMessageHandler(w, r)
} else {
http.Error(w, fmt.Sprintf("unsupported method %s", r.Method), http.StatusMethodNotAllowed)
}
}

func (s *guestbookServer) getMessagesHandler(w http.ResponseWriter, r *http.Request) {
entries, err := s.db.entries(r.Context())
if err != nil {
http.Error(w, fmt.Sprintf("failed to read entries: %+v", err), http.StatusInternalServerError)
// TODO return JSON error
return
}
if err := json.NewEncoder(w).Encode(entries); err != nil {
log.Printf("WARNING: failed to encode json into response: %+v", err)
} else {
log.Printf("%d entries returned", len(entries))
}
}

func (s *guestbookServer) postMessageHandler(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()

var v guestbookEntry
if err := json.NewDecoder(r.Body).Decode(&v); err != nil {
http.Error(w, fmt.Sprintf("failed to decode request body into json: %+v", err), http.StatusBadRequest)
return
}
if v.Author == "" {
http.Error(w, "empty 'author' value", http.StatusBadRequest)
return
}
if v.Message == "" {
http.Error(w, "empty 'message' value", http.StatusBadRequest)
return
}

v.Date = time.Now()

if err := s.db.addEntry(r.Context(), v); err != nil {
http.Error(w, fmt.Sprintf("failed to save entry: %+v", err), http.StatusInternalServerError)
return
}
log.Printf("entry saved: author=%q message=%q", v.Author, v.Message)
}
53 changes: 53 additions & 0 deletions integration/examples/multiple-renderers/backend/src/mongodb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package main

import (
"context"
"fmt"
"time"

"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)

type mongodb struct {
conn *mongo.Client
}

func (m *mongodb) entries(ctx context.Context) ([]guestbookEntry, error) {
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()

col := m.conn.Database("guestbook").Collection("entries")
cur, err := col.Find(ctx, bson.D{}, &options.FindOptions{
Sort: map[string]interface{}{"_id": -1},
})
if err != nil {
return nil, fmt.Errorf("mongodb.Find failed: %+v", err)
}
defer cur.Close(ctx)

var out []guestbookEntry
for cur.Next(ctx) {
var v guestbookEntry
if err := cur.Decode(&v); err != nil {
return nil, fmt.Errorf("decoding mongodb record failed: %+v", err)
}
out = append(out, v)
}
if err := cur.Err(); err != nil {
return nil, fmt.Errorf("failed to iterate on mongodb cursor: %+v", err)
}
return out, nil
}

func (m *mongodb) addEntry(ctx context.Context, e guestbookEntry) error {
ctx, cancel := context.WithTimeout(ctx, time.Second*3)
defer cancel()

col := m.conn.Database("guestbook").Collection("entries")
if _, err := col.InsertOne(ctx, e); err != nil {
return fmt.Errorf("mongodb.InsertOne failed: %+v", err)
}
return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}
labels:
app: {{ .Chart.Name }}
spec:
selector:
matchLabels:
app: {{ .Chart.Name }}
replicas: {{ .Values.replicaCount }}
template:
metadata:
labels:
app: {{ .Chart.Name }}
spec:
containers:
- name: {{ .Chart.Name }}
image: {{ .Values.image }}
ports:
- name: http-server
containerPort: 8081
- name: debug
containerPort: 3000
env:
- name: PORT
value: "8081"
- name: GUESTBOOK_DB_ADDR
value: go-guestbook-mongodb:27017
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
################################################################################
# WARNING: This MongoDB deployment is not suitable for production as the data is
# not persistently stored and will go away every time the Pod restarts. Consider
# using a Helm chart that provisions a StatefulSet instead of Deployment.
################################################################################
kind: Deployment
apiVersion: apps/v1
metadata:
name: go-guestbook-mongodb
labels:
app: guestbook
tier: db
spec:
replicas: 1
selector:
matchLabels:
app: guestbook
tier: db
template:
metadata:
labels:
app: guestbook
tier: db
spec:
containers:
- name: mongo
image: mongo:4
ports:
- containerPort: 27017
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: go-guestbook-mongodb
labels:
app: guestbook
tier: db
spec:
ports:
- port: 27017
targetPort: 27017
selector:
app: guestbook
tier: db
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}
labels:
app: {{ .Chart.Name }}
spec:
type: ClusterIP
selector:
app: {{ .Chart.Name }}
ports:
- port: 8081
targetPort: http-server
2 changes: 2 additions & 0 deletions integration/examples/multiple-renderers/backend/val.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# This is another YAML-formatted file.
image: go-guestbook-backend
3 changes: 3 additions & 0 deletions integration/examples/multiple-renderers/backend/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
labels:
app: frontend
spec:
selector:
matchLabels:
app: frontend
replicas: 1
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: go-guestbook-frontend
ports:
- name: http-server
containerPort: 8080
- name: debug
containerPort: 3000
env:
- name: PORT
value: "8080"
- name: GUESTBOOK_API_ADDR
value: localhost:27017
13 changes: 13 additions & 0 deletions integration/examples/multiple-renderers/frontend/k8s/service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
name: frontend
labels:
app: frontend
spec:
type: ClusterIP
selector:
app: frontend
ports:
- port: 8080
targetPort: http-server
Loading

0 comments on commit e7c05f5

Please sign in to comment.