Skip to content

Commit

Permalink
execute build and stream logs
Browse files Browse the repository at this point in the history
  • Loading branch information
damoon committed Nov 10, 2020
1 parent 8d9b8b2 commit 72b6ac2
Show file tree
Hide file tree
Showing 28 changed files with 1,748 additions and 8 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
tilt_modules
vendor
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
FROM alpine
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
# Wedding

Wedding accepts container image builds mocking the http interface of a docker daemon.\
It schedules builds via jobs to Kubernetes.\
Images are build using buildkit.
It schedules tasks as jobs to Kubernetes.\
Images are build using buildkit.\
Images are taged using skopeo.

This enables Tilt setups using gitlab ci without running a docker in docker daemon or exposing a host docker socket.\
This avoid to maintain a tilt configuration with (ci) and without (local dev) custom_build.
This enables running Tilt setups in gitlab pipelines without running a docker in docker daemon or exposing a host docker socket.\
This avoids to maintain a tilt configuration with (ci) and without (local dev) custom_build.

## Use case 1

Using docker cli to build and push an image.

``` bash
export DOCKER_HOST=tcp://wedding:2375
export DOCKER_HOST=tcp://wedding:2376
docker build -t registry/user/image:tag .
```

Expand All @@ -21,7 +22,7 @@ docker build -t registry/user/image:tag .
Using tilt to set up and test an environment.

``` bash
export DOCKER_HOST=tcp://wedding:2375
export DOCKER_HOST=tcp://wedding:2376
tilt ci
```

Expand All @@ -31,11 +32,11 @@ Using tilt to set up a development environment without a running local docker.

_Terminal 1_
``` bash
kubectl -n wedding port-forward svc/wedding 2375:2375
kubectl -n wedding port-forward svc/wedding 2376:2376
```

_Terminal 2_
``` bash
export DOCKER_HOST=tcp://127.0.07:2375
export DOCKER_HOST=tcp://127.0.07:2376
tilt up
```
75 changes: 75 additions & 0 deletions Tiltfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
disable_snapshots()
allow_k8s_contexts(['test', 'ci'])

load('ext://min_tilt_version', 'min_tilt_version')
min_tilt_version('0.15.0') # includes fix for auto_init+False with tilt ci

include('./services/Tiltfile')

k8s_yaml('deployment/kubernetes.yaml')

if os.environ.get('PROD', '') == '':
docker_build(
'wedding-image',
'.',
dockerfile='deployment/Dockerfile',
target='build-env',
build_args={"SOURCE_BRANCH":"development", "SOURCE_COMMIT":"development"},
only=[ 'go.mod'
, 'go.sum'
, 'pkg'
, 'cmd'
, 'deployment'
],
ignore=[
'.git',
'*/*_test.go',
'deployment/kubernetes.yaml',
],
live_update=[
sync('pkg', '/app/pkg'),
sync('cmd', '/app/cmd'),
sync('go.mod', '/app/go.mod'),
sync('go.sum', '/app/go.sum'),
run('go install ./cmd/wedding'),
],
)
else:
docker_build(
'backend-image',
'.',
dockerfile='deployment/Dockerfile',
build_args={"SOURCE_BRANCH":"development", "SOURCE_COMMIT":"development"},
only=[ 'go.mod'
, 'go.sum'
, 'pkg'
, 'cmd'
, 'deployment'
],
ignore=[
'.git',
'*/*_test.go',
'deployment/kubernetes.yaml',
],
)

k8s_resource(
'wedding',
port_forwards=['12376:2376'],
resource_deps=['setup-s3-bucket'],
)




#k8s_yaml('e2e-tests/deployment/tests.yaml')
#docker_build(
# 'e2e-tests-image',
# 'e2e-tests',
# dockerfile='e2e-tests/deployment/Dockerfile',
#)
#k8s_resource(
# 'e2e-tests',
# trigger_mode=TRIGGER_MODE_MANUAL,
# resource_deps=['backend', 'frontend'],
#)
219 changes: 219 additions & 0 deletions cmd/wedding/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
package main

import (
"context"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"os/signal"
"strings"
"syscall"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
wedding "github.com/damoon/wedding/pkg"
"github.com/urfave/cli/v2"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)

var (
gitHash string
gitRef string
)

func main() {
app := &cli.App{
Name: "Wedding",
Usage: "Serve dockerd API by running kubernetes jobs.",
Action: run,
Commands: []*cli.Command{
{
Name: "server",
Usage: "Start the server.",
Flags: []cli.Flag{
&cli.StringFlag{Name: "addr", Value: ":2376", Usage: "Address to run service on."},
&cli.StringFlag{Name: "s3-endpoint", Required: true, Usage: "s3 endpoint."},
&cli.StringFlag{Name: "s3-access-key-file", Required: true, Usage: "Path to s3 access key."},
&cli.StringFlag{Name: "s3-secret-key-file", Required: true, Usage: "Path to s3 secret access key."},
&cli.BoolFlag{Name: "s3-ssl", Value: true, Usage: "s3 uses SSL."},
&cli.StringFlag{Name: "s3-location", Value: "us-east-1", Usage: "s3 bucket location."},
&cli.StringFlag{Name: "s3-bucket", Required: true, Usage: "s3 bucket name."},
},
Action: run,
},
{
Name: "version",
Usage: "Show the version",
Action: func(c *cli.Context) error {
_, err := os.Stdout.WriteString(fmt.Sprintf("version: %s\ngit commit: %s", gitRef, gitHash))
if err != nil {
return err
}

return nil
},
},
},
}

err := app.Run(os.Args)
if err != nil {
log.Println(err)
os.Exit(1)
}
}

func run(c *cli.Context) error {
log.Printf("version: %v", gitRef)
log.Printf("git commit: %v", gitHash)

log.Println("set up storage")

storage, err := setupObjectStore(
c.String("s3-endpoint"),
c.String("s3-access-key-file"),
c.String("s3-secret-key-file"),
c.Bool("s3-ssl"),
c.String("s3-location"),
c.String("s3-bucket"))
if err != nil {
return fmt.Errorf("setup minio s3 client: %v", err)
}

log.Println("set up kubernetes client")

kubernetesClient, namespace, err := setupKubernetesClient()
if err != nil {
return fmt.Errorf("setup kubernetes client: %v", err)
}

log.Println("set up service")

svc := wedding.NewService(storage, kubernetesClient, namespace)

svcServer := httpServer(svc, c.String("addr"))

log.Println("starting server")

go mustListenAndServe(svcServer)

log.Println("running")

awaitShutdown()

ctx, cancel := context.WithTimeout(context.Background(), time.Hour)
defer cancel()
// TODO configure pod to allow a grace period of one hour

err = shutdown(ctx, svcServer)
if err != nil {
return fmt.Errorf("shutdown service server: %v", err)
}

log.Println("shutdown complete")

return nil
}

func setupObjectStore(
endpoint, accessKeyPath, secretKeyPath string,
useSSL bool,
region, bucket string,
) (*wedding.ObjectStore, error) {
accessKeyBytes, err := ioutil.ReadFile(accessKeyPath)
if err != nil {
return nil, fmt.Errorf("reading secret access key from %s: %v", accessKeyPath, err)
}

secretKeyBytes, err := ioutil.ReadFile(secretKeyPath)
if err != nil {
return nil, fmt.Errorf("reading secret access key from %s: %v", secretKeyPath, err)
}

accessKey := strings.TrimSpace(string(accessKeyBytes))
secretKey := strings.TrimSpace(string(secretKeyBytes))

endpointProtocol := "http"
if useSSL {
endpointProtocol = "https"
}

s3Config := &aws.Config{
Credentials: credentials.NewStaticCredentials(accessKey, secretKey, ""),
Endpoint: aws.String(fmt.Sprintf("%s://%s", endpointProtocol, endpoint)),
Region: aws.String(region),
DisableSSL: aws.Bool(!useSSL),
S3ForcePathStyle: aws.Bool(true),
}

newSession, err := session.NewSession(s3Config)
if err != nil {
return nil, fmt.Errorf("set up aws session: %v", err)
}

s3Client := s3.New(newSession)

return &wedding.ObjectStore{
Client: s3Client,
Bucket: bucket,
}, nil
}

func setupKubernetesClient() (*kubernetes.Clientset, string, error) {
ns, err := ioutil.ReadFile("/run/secrets/kubernetes.io/serviceaccount/namespace")
if err != nil {
return nil, "", fmt.Errorf("read namespace: %v", err)
}

config, err := rest.InClusterConfig()
if err != nil {
return nil, "", err
}

clientset, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, "", err
}

return clientset, string(ns), nil
}

func httpServer(h http.Handler, addr string) *http.Server {
httpServer := &http.Server{
// TODO do not timeout builds
ReadTimeout: 30 * time.Minute,
WriteTimeout: 30 * time.Minute,
}
httpServer.Addr = addr
httpServer.Handler = h

return httpServer
}

func mustListenAndServe(srv *http.Server) {
err := srv.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
}

func awaitShutdown() {
stop := make(chan os.Signal, 2)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
<-stop
}

func shutdown(ctx context.Context, srv *http.Server) error {
err := srv.Shutdown(ctx)
if err != nil {
return err
}

return nil
}
31 changes: 31 additions & 0 deletions deployment/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# build-env ###################################################
FROM golang:1.15.4 AS build-env
WORKDIR /app

RUN apt-get update
RUN apt-get install -y entr

COPY deployment/entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

COPY go.mod .
COPY go.sum .
RUN go mod download

ARG SOURCE_BRANCH
ARG SOURCE_COMMIT

COPY cmd cmd
COPY pkg pkg
RUN go install -ldflags="-X main.gitRef=${SOURCE_BRANCH} -X main.gitHash=${SOURCE_COMMIT}" ./cmd/wedding

###############################################################
FROM debian:buster

RUN apt-get update && apt-get install -y \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*

COPY --from=build-env /etc/mime.types /etc/mime.types
COPY --from=build-env /go/bin/wedding /usr/local/bin/wedding
ENTRYPOINT [ "wedding", "server" ]
2 changes: 2 additions & 0 deletions deployment/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/sh
find /go/bin/wedding | entr -d -r wedding server $@
Loading

0 comments on commit 72b6ac2

Please sign in to comment.