Skip to content

Commit d5611a3

Browse files
Initial commit
0 parents  commit d5611a3

File tree

8 files changed

+930
-0
lines changed

8 files changed

+930
-0
lines changed

Dockerfile

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
FROM golang:1.16.3 as build
2+
3+
RUN mkdir /go/app
4+
5+
COPY ./ /go/app
6+
7+
WORKDIR /go/app
8+
9+
RUN CGO_ENABLED=0 go build -o ./bin/server .
10+
11+
FROM alpine:latest
12+
13+
RUN mkdir /app
14+
COPY --from=build /go/app/bin/server /app/server
15+
16+
ENTRYPOINT ["/app/server"]

Makefile

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
build_docker:
2+
docker build -t nicholasjackson/connect-native:v0.0.3 .
3+
4+
push_docker: build_docker
5+
docker push nicholasjackson/connect-native:v0.0.3

go.mod

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module github.com/service-mesh-patterns/agentless-proxy-app
2+
3+
go 1.15
4+
5+
require (
6+
github.com/hashicorp/consul v1.8.0
7+
//github.com/hashicorp/consul v0.0.0-20210331183933-6e69829edbde
8+
github.com/hashicorp/consul/api v1.8.0
9+
github.com/hashicorp/go-hclog v0.14.1
10+
github.com/nicholasjackson/env v0.6.0
11+
)
12+
13+
//replace github.com/hashicorp/consul/api v1.8.0 => github.com/hashicorp/consul/api v0.0.0-20210331183933-6e69829edbde

go.sum

+648
Large diffs are not rendered by default.

main.go

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
"os"
8+
"os/signal"
9+
"syscall"
10+
"time"
11+
12+
"github.com/hashicorp/consul/api"
13+
"github.com/hashicorp/consul/connect"
14+
"github.com/hashicorp/go-hclog"
15+
"github.com/nicholasjackson/env"
16+
)
17+
18+
var name = env.String("SERVICE_NAME", false, "app", "Name of the service")
19+
var bindAddress = env.String("BIND_ADDRESS", false, "0.0.0.0", "Bind address of the service")
20+
var bindPort = env.Int("BIND_PORT", false, 9090, "Bind port of the service")
21+
var upstream = env.String("UPSTREAM", false, "", "Upstream service to call when the service receives an inbound request")
22+
var public = env.Bool("PUBLIC", false, false, "Does the service have a public API, if false the server is configured to use mTLS")
23+
var address = env.String("IP_ADDRESS", false, "localhost", "IP address of the service")
24+
25+
func main() {
26+
log := hclog.Default()
27+
28+
err := env.Parse()
29+
if err != nil {
30+
log.Error("Unable to parse environment", "error", err)
31+
os.Exit(1)
32+
}
33+
34+
// // Create a Consul API client
35+
client, err := api.NewClient(api.DefaultConfig())
36+
if err != nil {
37+
log.Error("Unable to create Consul client", "error", err)
38+
os.Exit(1)
39+
}
40+
41+
// register the service
42+
serviceID := fmt.Sprintf("%s-%d", *name, time.Now().UnixNano())
43+
client.Agent().ServiceRegister(&api.AgentServiceRegistration{
44+
Name: *name,
45+
ID: serviceID,
46+
Address: *address,
47+
Port: *bindPort,
48+
Connect: &api.AgentServiceConnect{
49+
Native: true,
50+
},
51+
})
52+
53+
// Create an instance representing this service. "my-service" is the
54+
// name of _this_ service. The service should be cleaned up via Close.
55+
svc, err := connect.NewService(*name, client)
56+
if err != nil {
57+
log.Error("Unable to register service", "error", err)
58+
os.Exit(1)
59+
}
60+
defer svc.Close()
61+
62+
// register the handler
63+
http.HandleFunc("/", handleRoot(svc, *upstream, log))
64+
65+
log.Info("Starting server", "address", *bindAddress, "port", *bindPort)
66+
if !*public {
67+
// The service is not public so create a HTTP server that serves via Connect
68+
server := &http.Server{
69+
Addr: fmt.Sprintf("%s:%d", *bindAddress, *bindPort),
70+
TLSConfig: svc.ServerTLSConfig(),
71+
Handler: http.DefaultServeMux,
72+
// ... other standard fields
73+
}
74+
75+
go server.ListenAndServeTLS("", "")
76+
} else {
77+
// Start a public server
78+
go http.ListenAndServe(fmt.Sprintf("%s:%d", *bindAddress, *bindPort), nil)
79+
}
80+
81+
sigs := make(chan os.Signal, 1)
82+
done := make(chan bool, 1)
83+
84+
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
85+
86+
go func() {
87+
sig := <-sigs
88+
fmt.Println()
89+
fmt.Println(sig)
90+
done <- true
91+
}()
92+
93+
fmt.Println("awaiting signal")
94+
<-done
95+
fmt.Println("exiting")
96+
97+
client.Agent().ServiceDeregister(serviceID)
98+
99+
}
100+
101+
type Response struct {
102+
Service string `json:"service"`
103+
UpstreamCall *Response `json:"upstream_call,omitempty"`
104+
}
105+
106+
func handleRoot(svc *connect.Service, upstream string, log hclog.Logger) func(rw http.ResponseWriter, r *http.Request) {
107+
return func(rw http.ResponseWriter, r *http.Request) {
108+
resp := Response{Service: svc.Name()}
109+
110+
if upstream != "" {
111+
r, err := svc.HTTPClient().Get(upstream)
112+
if err != nil || r == nil || r.StatusCode != http.StatusOK {
113+
http.Error(rw, fmt.Sprintf("Unable to contact upstream, error: %s", err), http.StatusInternalServerError)
114+
return
115+
}
116+
117+
upstreamResponse := &Response{}
118+
err = json.NewDecoder(r.Body).Decode(upstreamResponse)
119+
if err != nil {
120+
http.Error(rw, "Unable to decode upstream response", http.StatusInternalServerError)
121+
return
122+
}
123+
124+
resp.UpstreamCall = upstreamResponse
125+
}
126+
127+
err := json.NewEncoder(rw).Encode(resp)
128+
if err != nil {
129+
log.Error("Unable to write response", "error", err)
130+
}
131+
}
132+
}

shipyard/k8s_config/api.yaml

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: api-deployment
5+
labels:
6+
app: api
7+
spec:
8+
replicas: 1
9+
selector:
10+
matchLabels:
11+
app: api
12+
template:
13+
metadata:
14+
labels:
15+
app: api
16+
spec:
17+
containers:
18+
- name: api
19+
image: nicholasjackson/connect-native:v0.0.3
20+
ports:
21+
- containerPort: 9090
22+
env:
23+
- name: HOST_IP
24+
valueFrom:
25+
fieldRef:
26+
fieldPath: status.hostIP
27+
- name: POD_IP
28+
valueFrom:
29+
fieldRef:
30+
fieldPath: status.podIP
31+
- name: CONSUL_HTTP_ADDR
32+
value: http://$(HOST_IP):8500
33+
- name: SERVICE_NAME
34+
value: api
35+
- name: PUBLIC
36+
value: "true"
37+
- name: UPSTREAM
38+
value: https://payments.service.consul
39+
- name: IP_ADDRESS
40+
value: $(POD_IP)

shipyard/k8s_config/payments.yaml

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: payments-deployment
5+
labels:
6+
app: payments
7+
spec:
8+
replicas: 3
9+
selector:
10+
matchLabels:
11+
app: payments
12+
template:
13+
metadata:
14+
labels:
15+
app: payments
16+
spec:
17+
containers:
18+
- name: payments
19+
image: nicholasjackson/connect-native:v0.0.3
20+
ports:
21+
- containerPort: 9090
22+
env:
23+
- name: HOST_IP
24+
valueFrom:
25+
fieldRef:
26+
fieldPath: status.hostIP
27+
- name: POD_IP
28+
valueFrom:
29+
fieldRef:
30+
fieldPath: status.podIP
31+
- name: CONSUL_HTTP_ADDR
32+
value: http://$(HOST_IP):8500
33+
- name: SERVICE_NAME
34+
value: payments
35+
- name: IP_ADDRESS
36+
value: $(POD_IP)

shipyard/main.hcl

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
variable "consul_k8s_cluster" {
2+
default = "dc1"
3+
}
4+
5+
variable "consul_k8s_network" {
6+
default = "dc1"
7+
}
8+
9+
k8s_cluster "dc1" {
10+
driver = "k3s"
11+
12+
nodes = 1
13+
14+
network {
15+
name = "network.dc1"
16+
}
17+
}
18+
19+
network "dc1" {
20+
subnet = "10.5.0.0/16"
21+
}
22+
23+
module "consul" {
24+
source = "github.com/shipyard-run/blueprints/modules/kubernetes-consul"
25+
}
26+
27+
k8s_config "app" {
28+
depends_on = ["module.consul"]
29+
30+
cluster = "k8s_cluster.dc1"
31+
paths = [
32+
"./k8s_config/",
33+
]
34+
35+
wait_until_ready = true
36+
}
37+
38+
output "KUBECONFIG" {
39+
value = k8s_config("dc1")
40+
}

0 commit comments

Comments
 (0)