Skip to content

Commit a62527e

Browse files
author
smothiki
committed
WIP(builder): seperate code for various types of builders
1 parent 2bfd152 commit a62527e

65 files changed

Lines changed: 4681 additions & 0 deletions

Some content is hidden

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

Makefile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ build:
3636
mkdir -p ${BINDIR}/bin
3737
docker run --rm -v ${PWD}:/app -w /app golang:1.5.1 make docker-compile
3838

39+
build-bpb:
40+
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 godep go build -a -installsuffix cgo -ldflags '-s' -o $(BINARY_DEST_DIR)/builder cli/builder.go || exit 1
41+
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 godep go build -a -installsuffix cgo -ldflags '-s' -o $(BINARY_DEST_DIR)/fetcher fetcher/fetcher.go || exit 1
42+
@$(call check-static-binary,$(BINARY_DEST_DIR)/builder)
43+
@$(call check-static-binary,$(BINARY_DEST_DIR)/fetcher)
44+
for i in $(BINARIES); do \
45+
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 godep go build -a -installsuffix cgo -ldflags '-s' -o $(BINARY_DEST_DIR)/$$i src/$$i.go || exit 1; \
46+
done
47+
@for i in $(BINARIES); do \
48+
$(call check-static-binary,$(BINARY_DEST_DIR)/$$i); \
49+
done
50+
docker build -t $(IMAGE) rootfs
3951
# For cases where build is run inside of a container.
4052
docker-compile:
4153
go build -o ${BINDIR}/bin/boot -a -installsuffix cgo -ldflags ${LDFLAGS} boot.go

api/README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Deis Builder
2+
3+
Builder creates Docker images to be run elsewhere on the Deis platform.
4+
Builder itself also runs in a Docker container.
5+
6+
This Docker image is based on the official
7+
[alpine:3.1](https://registry.hub.docker.com/_/alpine/) image.
8+
9+
Please add any [issues](https://github.com/deis/deis/issues) you find with this software to
10+
the [Deis Project](https://github.com/deis/deis).
11+
12+
## Usage
13+
14+
Please consult the [Makefile](Makefile) for current instructions on how to build, test, push,
15+
install, and start **deis/builder**.
16+
17+
## Environment Variables
18+
19+
* **DEBUG** enables verbose output if set
20+
* **ETCD_PORT** sets the TCP port on which to connect to the local etcd
21+
daemon (default: *4001*)
22+
* **ETCD_PATH** sets the etcd directory where the builder announces
23+
its configuration (default: */deis/builder*)
24+
* **ETCD_TTL** sets the time-to-live before etcd purges a configuration
25+
value, in seconds (default: *10*)
26+
* **PORT** sets the TCP port on which the builder listens (default: *2222*)
27+
28+
## License
29+
30+
© 2015 Engine Yard, Inc.
31+
32+
Licensed under the Apache License, Version 2.0 (the "License"); you may
33+
not use this file except in compliance with the License. You may obtain
34+
a copy of the License at <http://www.apache.org/licenses/LICENSE-2.0>
35+
36+
Unless required by applicable law or agreed to in writing, software
37+
distributed under the License is distributed on an "AS IS" BASIS,
38+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
39+
See the License for the specific language governing permissions and
40+
limitations under the License.

api/builder.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Package builder provides libraries for the Deis builder.
2+
//
3+
// The Deis builder is responsible for packaging Docker images for consumers.
4+
//
5+
// The builder/cli package contains command line clients for this library.
6+
package builder
7+
8+
import (
9+
"github.com/Masterminds/cookoo"
10+
clog "github.com/Masterminds/cookoo/log"
11+
"github.com/deis/deis/buildpack/builder/sshd"
12+
13+
"log"
14+
"os"
15+
)
16+
17+
// Return codes that will be sent to the shell.
18+
const (
19+
StatusOk = iota
20+
StatusLocalError
21+
)
22+
23+
// Run starts the Builder service.
24+
//
25+
// The Builder service is responsible for setting up the local container
26+
// environment and then listening for new builds. The main listening service
27+
// is SSH. Builder listens for new Git commands and then sends those on to
28+
// Git.
29+
//
30+
// Run returns on of the Status* status code constants.
31+
func Run(cmd string) int {
32+
reg, router, ocxt := cookoo.Cookoo()
33+
log.SetFlags(0) // Time is captured elsewhere.
34+
35+
// We layer the context to add better logging and also synchronize
36+
// access so that goroutines don't get into race conditions.
37+
cxt := cookoo.SyncContext(ocxt)
38+
cxt.Put("cookoo.Router", router)
39+
cxt.AddLogger("stdout", os.Stdout)
40+
41+
// Build the routes. See routes.go.
42+
routes(reg)
43+
44+
// Bootstrap the background services. If this fails, we stop.
45+
if err := router.HandleRequest("boot", cxt, false); err != nil {
46+
clog.Errf(cxt, "Fatal errror on boot: %s", err)
47+
return StatusLocalError
48+
}
49+
50+
// Set up the SSH service.
51+
ip := os.Getenv("SSH_HOST_IP")
52+
if ip == "" {
53+
ip = "0.0.0.0"
54+
}
55+
port := os.Getenv("SSH_HOST_PORT")
56+
if port == "" {
57+
port = "2223"
58+
}
59+
60+
cxt.Put(sshd.Address, ip+":"+port)
61+
62+
// Supply route names for handling various internal routing. While this
63+
// isn't necessary for Cookoo, it makes it easy for us to mock these
64+
// routes in tests. c.f. sshd/server.go
65+
cxt.Put("route.sshd.pubkeyAuth", "pubkeyAuth")
66+
cxt.Put("route.sshd.sshPing", "sshPing")
67+
cxt.Put("route.sshd.sshGitReceive", "sshGitReceive")
68+
69+
// Start the SSH service.
70+
// TODO: We could refactor Serve to be a command, and then run this as
71+
// a route.
72+
if err := sshd.Serve(reg, router, cxt); err != nil {
73+
clog.Errf(cxt, "SSH server failed: %s", err)
74+
return StatusLocalError
75+
}
76+
77+
return StatusOk
78+
}

api/cli/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# The Deis Builder CLI
2+
3+
This code is built by `make` and installed into the `rootfs`.

api/cli/builder.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package main
2+
3+
import (
4+
"os"
5+
"runtime"
6+
7+
"github.com/deis/deis/buildpack/builder"
8+
)
9+
10+
func main() {
11+
runtime.GOMAXPROCS(runtime.NumCPU())
12+
os.Exit(builder.Run("boot"))
13+
}

api/commands.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package builder
2+
3+
import (
4+
"os"
5+
"os/signal"
6+
"time"
7+
8+
"github.com/Masterminds/cookoo"
9+
"github.com/Masterminds/cookoo/log"
10+
"github.com/Masterminds/cookoo/safely"
11+
)
12+
13+
// Sleep delays the execution of the remainder of the chain of commands.
14+
//
15+
// Params:
16+
// -duration (time.Duration): Time to sleep.
17+
// -message (string): The message to log when entering sleep.
18+
func Sleep(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
19+
dur := p.Get("duration", 10*time.Millisecond).(time.Duration)
20+
msg := p.Get("messages", "Sleeping").(string)
21+
log.Info(c, msg)
22+
time.Sleep(dur)
23+
log.Info(c, "Woke up.")
24+
return true, nil
25+
}
26+
27+
// KillOnExit kills PIDs when the program exits.
28+
//
29+
// Otherwise, this blocks until an os.Interrupt or os.Kill is received.
30+
//
31+
// Params:
32+
// This treats Params as a map of process names (unimportant) to PIDs. It then
33+
// attempts to kill all of the pids that it receives.
34+
func KillOnExit(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
35+
sigs := make(chan os.Signal, 1)
36+
signal.Notify(sigs, os.Interrupt, os.Kill)
37+
38+
safely.GoDo(c, func() {
39+
log.Info(c, "Builder is running.")
40+
41+
<-sigs
42+
43+
c.Log("info", "Builder received signal to stop.")
44+
pids := p.AsMap()
45+
killed := 0
46+
for name, pid := range pids {
47+
if pid, ok := pid.(int); ok {
48+
if proc, err := os.FindProcess(pid); err == nil {
49+
log.Infof(c, "Killing %s (pid=%d)", name, pid)
50+
proc.Kill()
51+
killed++
52+
}
53+
}
54+
}
55+
})
56+
return nil, nil
57+
}

api/commands_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package builder
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/Masterminds/cookoo"
8+
)
9+
10+
func TestSleep(t *testing.T) {
11+
reg, router, cxt := cookoo.Cookoo()
12+
13+
reg.Route("test", "Test route").
14+
Does(Sleep, "res").Using("duration").WithDefault(3 * time.Second)
15+
16+
start := time.Now()
17+
if err := router.HandleRequest("test", cxt, true); err != nil {
18+
t.Error(err)
19+
}
20+
21+
end := time.Now()
22+
if end.Sub(start) < 3*time.Second {
23+
t.Error("expected elapsed time to be 3 seconds.")
24+
}
25+
26+
}

api/confd/confd.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Package confd provides basic Confd support.
2+
//
3+
// Right now, this library is highly specific to the needs of the present
4+
// builder. Because the confd library is not all public, we don't use it directly.
5+
// Instead, we invoke the CLI.
6+
package confd
7+
8+
import (
9+
"fmt"
10+
"os"
11+
"os/exec"
12+
"strconv"
13+
"time"
14+
15+
"github.com/Masterminds/cookoo"
16+
"github.com/Masterminds/cookoo/log"
17+
"github.com/Masterminds/cookoo/safely"
18+
)
19+
20+
// defaultEtcd is the default Etcd host.
21+
const defaultEtcd = "127.0.0.1:4001"
22+
23+
// RunOnce runs the equivalent of `confd --onetime`.
24+
//
25+
// This may run the process repeatedly until either we time out (~20 minutes) or
26+
// the templates are successfully built.
27+
//
28+
// Importantly, this blocks until the run is complete.
29+
//
30+
// Params:
31+
// - node (string): The etcd node to use. (Only etcd is currently supported)
32+
//
33+
// Returns:
34+
// - The []bytes from stdout and stderr when running the program.
35+
//
36+
func RunOnce(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
37+
node := p.Get("node", defaultEtcd).(string)
38+
39+
dargs := []string{"-onetime", "-node", node, "-log-level", "error"}
40+
41+
log.Info(c, "Building confd templates. This may take a moment.")
42+
43+
limit := 1200
44+
timeout := time.Second * 3
45+
var lasterr error
46+
start := time.Now()
47+
for i := 0; i < limit; i++ {
48+
if out, err := exec.Command("confd", dargs...).CombinedOutput(); err == nil {
49+
log.Infof(c, "Templates generated for %s on run %d", node, i)
50+
return out, nil
51+
} else {
52+
log.Debugf(c, "Recoverable error: %s", err)
53+
log.Debugf(c, "Output: %q", out)
54+
lasterr = err
55+
}
56+
57+
time.Sleep(timeout)
58+
log.Infof(c, "Re-trying template build. (Elapsed time: %d)", time.Now().Sub(start)/time.Second)
59+
}
60+
61+
return nil, fmt.Errorf("Could not build confd templates before timeout. Last error: %s", lasterr)
62+
}
63+
64+
// Run starts confd and runs it in the background.
65+
//
66+
// If the command fails immediately on startup, an error is immediately
67+
// returned. But from that point, a goroutine watches the command and
68+
// reports if the command dies.
69+
//
70+
// Params:
71+
// - node (string): The etcd node to use. (Only etcd is currently supported)
72+
// - interval (int, default:5): The rebuilding interval.
73+
//
74+
// Returns
75+
// bool true if this succeeded.
76+
func Run(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
77+
node := p.Get("node", defaultEtcd).(string)
78+
interval := strconv.Itoa(p.Get("interval", 5).(int))
79+
80+
cmd := exec.Command("confd", "-log-level", "error", "-node", node, "-interval", interval)
81+
if err := cmd.Start(); err != nil {
82+
return false, err
83+
}
84+
85+
log.Infof(c, "Watching confd.")
86+
safely.Go(func() {
87+
if err := cmd.Wait(); err != nil {
88+
// If confd exits, builder will stop functioning as intended. So
89+
// we stop builder and let the environment restart.
90+
log.Errf(c, "Stopping builder. confd exited with error: %s", err)
91+
os.Exit(37)
92+
}
93+
})
94+
95+
return true, nil
96+
}

api/confd/confd_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package confd
2+
3+
import (
4+
"testing"
5+
6+
"github.com/Masterminds/cookoo"
7+
)
8+
9+
func TestRunOnce(t *testing.T) {
10+
reg, _, _ := cookoo.Cookoo()
11+
12+
reg.Route("test", "Test route").
13+
Does(RunOnce, "res").
14+
Using("node").WithDefault("localhost:4001")
15+
}
16+
func TestRun(t *testing.T) {
17+
reg, _, _ := cookoo.Cookoo()
18+
19+
reg.Route("test", "Test route").
20+
Does(Run, "res").
21+
Using("node").WithDefault("localhost:4001").
22+
Using("interval").WithDefault(200)
23+
24+
}

0 commit comments

Comments
 (0)