Skip to content

Commit

Permalink
Merge pull request #16 from sylphon/add-event-messages
Browse files Browse the repository at this point in the history
Add event messages
  • Loading branch information
Rafe Colton committed Nov 28, 2014
2 parents 5d79d2a + 93439fa commit 5070332
Show file tree
Hide file tree
Showing 10 changed files with 282 additions and 119 deletions.
48 changes: 27 additions & 21 deletions builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ type Builder struct {
workdir string
nextSubSequence *parser.SubSequence
Stdout io.Writer
log comm.LogChan
status comm.StatusChan
reporter *comm.Reporter
Builderfile string
contextDir string
}
Expand All @@ -54,7 +53,7 @@ func (bob *Builder) SetNextSubSequence(subSeq *parser.SubSequence) {
// new builder
type NewBuilderOptions struct {
Log comm.LogChan
Status comm.StatusChan
Event comm.EventChan
ContextDir string
dockerClient dockerclient.DockerClient // default to nil for regular docker client
}
Expand All @@ -63,32 +62,35 @@ type NewBuilderOptions struct {
NewBuilder returns an instance of a Builder struct. The function exists in
case we want to initialize our Builders with something.
*/
func NewBuilder(opts NewBuilderOptions) (*Builder, error) {
func NewBuilder(opts NewBuilderOptions) *Builder {
var ret = &Builder{
log: opts.Log,
status: opts.Status,
reporter: comm.NewReporter(opts.Log, opts.Event),
contextDir: opts.ContextDir,
}

var client = opts.dockerClient
if client == nil {
var err error
client, err = dockerclient.NewDockerClient()
if err != nil {
return nil, err
}
}
ret.dockerClient = client
ret.dockerClient = opts.dockerClient

if opts.Log != nil {
ret.Stdout = comm.NewLogEntryWriter(opts.Log)
} else {
ret.Stdout = ioutil.Discard /* /dev/null */
}

return ret, nil
return ret
}

// BuildCommandSequence performs a build from a parser-generated CommandSequence struct
func (bob *Builder) BuildCommandSequence(commandSequence *parser.CommandSequence) error {
bob.reporter.Event(comm.EventOptions{EventType: comm.RequestedEvent})

if bob.dockerClient == nil {
client, err := dockerclient.NewDockerClient()
if err != nil {
return err
}
bob.dockerClient = client
}

for _, seq := range commandSequence.Commands {
var imageID string
var err error
Expand All @@ -101,7 +103,7 @@ func (bob *Builder) BuildCommandSequence(commandSequence *parser.CommandSequence
return err
}

bob.Log(
bob.reporter.Log(
l.WithField("container_section", seq.Metadata.Name),
"running commands for container section",
)
Expand All @@ -114,10 +116,11 @@ func (bob *Builder) BuildCommandSequence(commandSequence *parser.CommandSequence
SkipPush: SkipPush,
Stdout: bob.Stdout,
Workdir: bob.workdir,
Reporter: bob.reporter,
}
cmd = cmd.WithOpts(opts)

bob.Log(l.WithField("command", cmd.Message()), "running docker command")
bob.reporter.Log(l.WithField("command", cmd.Message()), "running docker command")

if imageID, err = cmd.Run(); err != nil {
switch err.(type) {
Expand All @@ -131,6 +134,9 @@ func (bob *Builder) BuildCommandSequence(commandSequence *parser.CommandSequence

bob.attemptToDeleteTemporaryUUIDTag(seq.Metadata.UUID)
}

bob.reporter.Event(comm.EventOptions{EventType: comm.CompletedEvent})

return nil
}

Expand All @@ -142,7 +148,7 @@ func (bob *Builder) attemptToDeleteTemporaryUUIDTag(uuid string) {
regex := ":" + uuid + "$"
image, err := bob.dockerClient.LatestImageByRegex(regex)
if err != nil {
bob.LogLevel(
bob.reporter.LogLevel(
l.WithField("err", err),
"error getting repo taggged with temporary tag",
l.WarnLevel,
Expand All @@ -155,7 +161,7 @@ func (bob *Builder) attemptToDeleteTemporaryUUIDTag(uuid string) {
return
}
if matched {
bob.Log(
bob.reporter.Log(
l.WithFields(l.Fields{
"image_id": image.ID,
"tag": tag,
Expand All @@ -164,7 +170,7 @@ func (bob *Builder) attemptToDeleteTemporaryUUIDTag(uuid string) {
)

if err = bob.dockerClient.Client().RemoveImage(tag); err != nil {
bob.LogLevel(
bob.reporter.LogLevel(
l.WithField("err", err),
"error deleting temporary tag",
l.WarnLevel,
Expand Down
18 changes: 4 additions & 14 deletions builder/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,12 @@ func TestBuildCommandSequence(t *testing.T) {
var p = parser.NewParser(opts)
commandSequence := p.Parse(unitConfig)

builder := &Builder{
contextDir: os.Getenv("GOPATH") + "/src/github.com/sylphon/builder-core/_testing",
}
builder := NewBuilder(NewBuilderOptions{
ContextDir: os.Getenv("GOPATH") + "/src/github.com/sylphon/builder-core/_testing",
dockerClient: dockerclient.FakeClient(),
})

if err := builder.BuildCommandSequence(commandSequence); err != nil {
t.Fatal(err)
}
}

func TestNewBuilder(t *testing.T) {
var ops = NewBuilderOptions{
ContextDir: os.Getenv("PWD"),
dockerClient: dockerclient.FakeClient(),
}
_, err := NewBuilder(ops)
if err != nil {
t.Fatal(err)
}
}
20 changes: 0 additions & 20 deletions builder/log.go

This file was deleted.

12 changes: 12 additions & 0 deletions communication/channels.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package comm

type (
// LogChan is a channel for log entries
LogChan chan LogEntry

// EventChan is a channel for status updates
EventChan chan Event

// ExitChan is a channel for receiving the final exit value (error or nil)
ExitChan chan error
)
67 changes: 67 additions & 0 deletions communication/event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package comm

// EventType is a type for constants that indicate the type of event reported
type EventType uint8

// Event is the type that will be sent over the event channel
type Event interface {
EventType() EventType
Data() map[string]interface{}
}

const (
// RequestedEvent is for when a build is initially requested
RequestedEvent EventType = iota

// BuildEvent is for when a `docker build` command starts
BuildEvent

// BuildCompletedEvent is for when a `docker build` command completes
BuildCompletedEvent

// TagEvent is for when a `docker tag` command starts
TagEvent

// TagCompletedEvent is for when a `docker tag` command finishes
TagCompletedEvent

// PushEvent is for when a `docker push` command starts
PushEvent

// PushCompletedEvent is for when a `docker push` command finishes
PushCompletedEvent

// CompletedEvent is for whe nthe entire build finishes (corresopnds to a RequestedEvent)
CompletedEvent
)

func (t EventType) String() string {
switch t {
case RequestedEvent:
return "RequestedEvent"
case CompletedEvent:
return "CompletedEvent"
case BuildEvent:
return "BuildEvent"
case BuildCompletedEvent:
return "BuildCompletedEvent"
case TagEvent:
return "TagEvent"
case TagCompletedEvent:
return "TagCompletedEvent"
case PushEvent:
return "PushEvent"
case PushCompletedEvent:
return "PushCompletedEvent"
default:
return ""
}
}

type event struct {
eventType EventType
data map[string]interface{}
}

func (e *event) EventType() EventType { return e.eventType }
func (e *event) Data() map[string]interface{} { return e.data }
32 changes: 1 addition & 31 deletions communication/entry.go → communication/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,6 @@ import (
"github.com/Sirupsen/logrus"
)

//type State uint8

//const (
//Requested State = iota
//Building
//Pushing
//Completed
//Errored
//)

type (
// LogChan is a channel for log entries
LogChan chan LogEntry

// StatusChan is a channel for status updates (somewhat TBD)
StatusChan chan StatusEntry

// ExitChan is a channel for receiving the final exit value (error or nil)
ExitChan chan error
)

// LogEntry is a convenient, extensible way to ship logrus log entries
type LogEntry interface {
Entry() *logrus.Entry
Expand All @@ -44,15 +23,6 @@ func (l *logEntry) Entry() *logrus.Entry {
return (*logrus.Entry)(l)
}

// StatusEntry is the tentative interface for the structs returned on the status channel
type StatusEntry interface {
BuildID() int
//PreviousState() State
//CurrentState() State
Note() string
//Error() error // should be checked for non-nil
}

// LogEntryWriter is a type for implementing the io.Writer interface
type LogEntryWriter interface {
io.Writer
Expand All @@ -71,7 +41,7 @@ type logEntryWriter struct {
func (writer logEntryWriter) Write(p []byte) (n int, err error) {
lines := strings.Split(string(p), "\n")
for _, line := range lines {
writer.log <- (*logEntry)(&logrus.Entry{Message: string(line)})
writer.log <- (*logEntry)(&logrus.Entry{Message: string(line), Level: logrus.DebugLevel})
}
return len(p), nil
}
49 changes: 49 additions & 0 deletions communication/reporter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package comm

import (
"github.com/Sirupsen/logrus"
)

// Reporter is type for sending messages on log and/or status channels
type Reporter struct {
log LogChan
event EventChan
}

// NewReporter returns a reporter that is initialized with the provided channels
func NewReporter(log LogChan, event EventChan) *Reporter {
return &Reporter{
log: log,
event: event,
}
}

// Log - send a log message into the ether
func (r *Reporter) Log(entry *logrus.Entry, message string) {
r.LogLevel(entry, message, logrus.DebugLevel)
}

// LogLevel - send a log message into the ether, specifying level
func (r *Reporter) LogLevel(entry *logrus.Entry, message string, level logrus.Level) {
entry.Message = message
entry.Level = level
if r.log != nil {
r.log <- NewLogEntry(entry)
}
}

// EventOptions are the options when telling a Reporter to trigger an event
type EventOptions struct {
EventType EventType
Data map[string]interface{}
}

// Event notifies the Reporter's EventChan that an event has occurred
func (r *Reporter) Event(opts EventOptions) {
if r.event != nil {
r.event <- &event{
eventType: opts.EventType,
data: opts.Data,
}
}
}
Loading

0 comments on commit 5070332

Please sign in to comment.