Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'master' into feature/ticket-instance-merge

Conflicts:
	proctype.go
  • Loading branch information...
commit 9a174c5bf20f13d45132a13d8074221e2f0317d0 2 parents b615bec + 43721ca
Alexis Sellier cloudhead authored
1  .gitignore
View
@@ -6,3 +6,4 @@ src/
tags
visor.rb-e
visor
+bin/
16 Makefile
View
@@ -4,6 +4,7 @@ GOBIN ?= $(GOPATH)/bin
LDFLAGS := -ldflags "-X main.VERSION $(VERSION)"
GOFLAGS := -x $(LDFLAGS)
PKGPATH := $(GOPATH)/src/github.com/soundcloud/visor
+GOARCH ?= amd64
# LOCAL #
@@ -20,6 +21,17 @@ $(PKGPATH):
mkdir -p $(shell dirname $(PKGPATH))
ln -sf $(PWD) $(PKGPATH)
+test:
+ go test
+
+# DIST #
+
+dist: linux darwin
+
+linux darwin:
+ GOOS=$@ CGO_ENABLED=0 GOARCH=$(GOARCH) go build $(LDFLAGS) -o bin/$@/visor ./cmd/visor
+ cd bin/$@ && tar -caf visor-v$(VERSION)-$@.tar.gz visor
+
# DEBIAN PACKAGING #
DEB_NAME=visor
@@ -31,7 +43,7 @@ DEB_MAINTAINER=Daniel Bornkessel <daniel@soundcloud.com>
include deb.mk
debroot:
- GOBIN=$(DEB_ROOT)/usr/bin $(MAKE)
+ GOBIN=$(DEB_ROOT)/usr/bin $(MAKE) install
# BUILD #
@@ -40,3 +52,5 @@ build: clean debroot debbuild
clean: debclean
GOPATH=$(GOPATH) go clean
rm -rf bin src pkg
+
+.PHONY: test
2  VERSION
View
@@ -1 +1 @@
-0.6.1
+0.6.3
2  conn.go
View
@@ -152,6 +152,8 @@ func (c *conn) Close() {
// Del is a wrapper around (*doozer.Conn).Del which also supports
// deleting directories.
+// TODO: Concurrent implementation
+// TODO: Better error messages on NOENT
func (c *conn) Del(path string, rev int64) (err error) {
path = c.prefixPath(path)
2  error.go
View
@@ -17,6 +17,8 @@ var (
ErrInvalidState = errors.New("invalid state")
ErrNoEnt = errors.New("file not found")
ErrBadPath = errors.New("invalid path: only ASCII letters, numbers, '.', or '-' are allowed")
+ ErrSchemaMism = errors.New("visor version not compatible with current coordinator schema")
+ ErrBadPtyName = errors.New("invalid proc type name: only alphanumeric chars allowed")
)
type Error struct {
2  event.go
View
@@ -14,6 +14,7 @@ import (
)
// An Event represents a change to a file in the registry.
+// TODO: turn `Emitter` into own type, instead of map
type Event struct {
Type EventType // Type of event
Emitter map[string]string // The parsed file path
@@ -23,6 +24,7 @@ type Event struct {
Rev int64
}
+// TODO: turn into string, remove `EventTypes`
type EventType int
var EventTypes = map[EventType]string{
1  file.go
View
@@ -49,6 +49,7 @@ func (f *file) Del() error {
}
// Create creates a file from its Value attribute
+// TODO: Rename to 'Save'
func (f *file) Create() (*file, error) {
return f.Set(f.Value)
}
7 proctype.go
View
@@ -9,9 +9,12 @@ import (
"errors"
"fmt"
"strconv"
+ "regexp"
"time"
)
+var reProcName = regexp.MustCompile("^[[:alnum:]]+$")
+
// ProcType represents a process type with a certain scale.
type ProcType struct {
dir
@@ -54,6 +57,10 @@ func (p *ProcType) Register() (ptype *ProcType, err error) {
return nil, ErrKeyConflict
}
+ if !reProcName.MatchString(p.Name) {
+ return nil, ErrBadPtyName
+ }
+
p.Port, err = ClaimNextPort(p.Snapshot)
if err != nil {
return nil, errors.New(fmt.Sprintf("couldn't claim port: %s", err.Error()))
26 proctype_test.go
View
@@ -49,6 +49,32 @@ func TestProcTypeRegister(t *testing.T) {
}
}
+func TestProcTypeRegisterWithInvalidName1(t *testing.T) {
+ s, app := proctypeSetup("reg1232")
+ pty := NewProcType(app, "who-op", s)
+
+ pty, err := pty.Register()
+ if err != ErrBadPtyName {
+ t.Errorf("invalid proc type name (who-op) did not raise error")
+ }
+ if err != ErrBadPtyName && err != nil {
+ t.Fatal("wrong error was raised for invalid proc type name")
+ }
+}
+
+func TestProcTypeRegisterWithInvalidName2(t *testing.T) {
+ s, app := proctypeSetup("reg1233")
+ pty := NewProcType(app, "who_op", s)
+
+ pty, err := pty.Register()
+ if err != ErrBadPtyName {
+ t.Errorf("invalid proc type name (who_op) did not raise error")
+ }
+ if err != ErrBadPtyName && err != nil {
+ t.Fatal("wrong error was raised for invalid proc type name")
+ }
+}
+
func TestProcTypeUnregister(t *testing.T) {
s, app := proctypeSetup("unreg123")
pty := NewProcType(app, "whoop", s)
75 schema.go
View
@@ -0,0 +1,75 @@
+package visor
+
+import (
+ "strconv"
+)
+
+const schemaPath = "/internal/schema"
+
+type SchemaEvent struct {
+ Version int
+}
+
+// WatchSchema notifies the specified ch channel on schema change,
+// and errch on error. If an error occures, WatchSchema exits.
+func WatchSchema(s Snapshot, ch chan SchemaEvent, errch chan error) {
+ rev := s.Rev
+ for {
+ ev, err := s.conn.Wait(schemaPath, rev+1)
+ if err != nil {
+ errch <- err
+ return
+ }
+ rev = ev.Rev
+
+ v, err := strconv.Atoi(string(ev.Body))
+ if err != nil {
+ errch <- err
+ return
+ }
+ ch <- SchemaEvent{v}
+ }
+}
+
+// VerifySchema checks that visor's schema version is compatible with
+// the coordinator's. If this is not the case, ErrSchemaMism is returned.
+//
+// See WatchSchema to get notified on schema change.
+func VerifySchema(s Snapshot) error {
+ return verifySchemaVersion(s, SchemaVersion)
+}
+
+func SetSchemaVersion(s Snapshot, version int) (newSnapshot Snapshot, err error) {
+ strVersion := strconv.Itoa(version)
+
+ newSnapshot, err = s.set(schemaPath, strVersion)
+
+ return
+}
+
+func verifySchemaVersion(s Snapshot, version int) error {
+ exists, _, err := s.exists(schemaPath)
+ if err != nil {
+ return err
+ }
+
+ if !exists {
+ return ErrSchemaMism
+ }
+
+ value, _, err := s.get(schemaPath)
+ if err != nil {
+ return err
+ }
+
+ intValue, err := strconv.Atoi(value)
+ if err != nil {
+ return err
+ }
+
+ if intValue != version {
+ return ErrSchemaMism
+ }
+
+ return nil
+}
146 schema_test.go
View
@@ -0,0 +1,146 @@
+// Copyright (c) 2012, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/soundcloud/visor
+
+package visor
+
+import (
+ "testing"
+ "time"
+)
+
+func cleanSchemaVersion(s Snapshot, t *testing.T) Snapshot {
+ var exists bool
+ var err error
+
+ if exists, _, err = s.exists(schemaPath); err != nil {
+ t.Fatal("error calling del on '%s': %s", schemaPath, err.Error())
+ }
+
+ if exists {
+ if err := s.del(schemaPath); err != nil {
+ t.Fatal("error calling get on '" + schemaPath + "': " + err.Error())
+ }
+ s = s.FastForward(-1)
+ }
+
+ return s
+}
+
+func TestSchemaMissing(t *testing.T) {
+ s, err := Dial(DefaultAddr, DefaultRoot)
+
+ if err != nil {
+ panic(err)
+ }
+
+ s = cleanSchemaVersion(s, t)
+
+ if err := VerifySchema(s); err != ErrSchemaMism {
+ if err == nil {
+ t.Error("missing schema version did not error out")
+ } else {
+ t.Error("missing schema version returned incorrect error: " + err.Error())
+ }
+ }
+}
+
+func TestSetVersion(t *testing.T) {
+ s, err := Dial(DefaultAddr, DefaultRoot)
+
+ if err != nil {
+ panic(err)
+ }
+
+ s = cleanSchemaVersion(s, t)
+
+ if s, err = SetSchemaVersion(s, SchemaVersion); err != nil {
+ t.Fatal("setting schema version failed")
+ }
+
+ if err := VerifySchema(s); err != nil {
+ t.Error("setting new version failed: " + err.Error())
+ }
+}
+
+func TestVersionTooNew(t *testing.T) {
+ s, err := Dial(DefaultAddr, DefaultRoot)
+ coordinatorVersion := 0
+
+ if err != nil {
+ panic(err)
+ }
+
+ s = cleanSchemaVersion(s, t)
+
+ if s, err = SetSchemaVersion(s, coordinatorVersion); err != nil {
+ t.Fatal("setting schema version failed")
+ }
+
+ if err := VerifySchema(s); err != ErrSchemaMism {
+ if err == nil {
+ t.Error("newer schema version did not error out")
+ } else {
+ t.Error("newer schema version returned incorrect error: " + err.Error())
+ }
+ }
+}
+
+func TestVersionTooOld(t *testing.T) {
+ s, err := Dial(DefaultAddr, DefaultRoot)
+ coordinatorVersion := 99
+
+ if err != nil {
+ panic(err)
+ }
+
+ s = cleanSchemaVersion(s, t)
+
+ if s, err = SetSchemaVersion(s, coordinatorVersion); err != nil {
+ t.Fatal("setting schema version failed")
+ }
+
+ if err := VerifySchema(s); err != ErrSchemaMism {
+ if err == nil {
+ t.Error("older schema version did not error out")
+ } else {
+ t.Error("older schema version returned incorrect error: " + err.Error())
+ }
+ }
+}
+
+func TestSchemaWatcher(t *testing.T) {
+ s, err := Dial(DefaultAddr, DefaultRoot)
+ coordinatorVersion := 2
+
+ if err != nil {
+ panic(err)
+ }
+
+ s = cleanSchemaVersion(s, t)
+
+ if s, err = SetSchemaVersion(s, coordinatorVersion); err != nil {
+ t.Fatal(err)
+ }
+
+ schemaEvents := make(chan SchemaEvent, 1)
+ errch := make(chan error, 1)
+
+ go WatchSchema(s, schemaEvents, errch)
+
+ go func() {
+ if s, err = SetSchemaVersion(s, coordinatorVersion+1); err != nil {
+ t.Fatal(err)
+ }
+ }()
+
+ select {
+ case <-time.After(time.Second):
+ t.Error("waiting for schema change timed out")
+ case e := <-errch:
+ t.Error(e)
+ case <-schemaEvents:
+ // Ok
+ }
+}
2  snapshot_test.go
View
@@ -136,7 +136,7 @@ func TestSnapshotGetScale(t *testing.T) {
s := snapshotSetup()
app := NewApp("scale-app", "git://scale.git", "scale-stack", s)
- pty := NewProcType(app, "scale-proc", s)
+ pty := NewProcType(app, "scaleproc", s)
rev := NewRevision(app, "scale-ref", s)
if _, err := app.Register(); err != nil {
2  visor.go
View
@@ -40,6 +40,8 @@ import (
"time"
)
+const SchemaVersion = 1
+
const (
DefaultUri string = "doozer:?ca=localhost:8046"
DefaultAddr string = "localhost:8046"
4 visor.rb
View
@@ -5,7 +5,7 @@ class Visor < Formula
url 'https://github.com/soundcloud/visor/zipball/master'
depends_on 'go'
skip_clean 'bin'
- version '0.6.0'
+ version '0.6.3'
def install
@@ -25,6 +25,7 @@ def install
exit 1
end
+ ENV['PATH'] = "/usr/local/bin:#{ENV['PATH']}"
begin
system("which hg")
rescue
@@ -51,6 +52,7 @@ def install
end
ENV['GOPATH'] = buildpath
ENV['GOBIN'] = "#{prefix}/bin"
+ ENV['PWD'] = buildpath
system "make install"
end
Please sign in to comment.
Something went wrong with that request. Please try again.