Commits · ampify

tav  ·  9b99d42f81f69781313d28b7b492a9c30ae9806f  ·  ampify  ·  github 1311828157

Implemented nodule building and testing.

Changes

src/amp/nodule/Makefile
......@@ -0,0 +1,10 @@
1
+# Public Domain (-) 2011 The Ampify Authors.
2
+# See the Ampify UNLICENSE file for details.
3
+
4
+include $(GOROOT)/src/Make.inc
5
+
6
+TARG=amp/nodule
7
+GOFILES=\
8
+        nodule.go\
9
+
10
+include $(GOROOT)/src/Make.pkg
src/amp/nodule/nodule.go
......@@ -0,0 +1,392 @@
1
+// Public Domain (-) 2011 The Ampify Authors.
2
+// See the Ampify UNLICENSE file for details.
3
+
4
+package nodule
5
+
6
+import (
7
+	"amp/logging"
8
+	"amp/master"
9
+	"amp/runtime"
10
+	"amp/yaml"
11
+	"bytes"
12
+	"exec"
13
+	"exp/template"
14
+	"fmt"
15
+	"net"
16
+	"os"
17
+	"path/filepath"
18
+	"strings"
19
+)
20
+
21
+// Find all nodules at or within the immediate subdirectories of the given
22
+// ``directory``.
23
+func Find(directory string) (nodules []*Nodule, err os.Error) {
24
+
25
+	directory, err = filepath.Abs(filepath.Clean(directory))
26
+	if err != nil {
27
+		return
28
+	}
29
+
30
+	root, err := os.Open(directory)
31
+	if err != nil {
32
+		return
33
+	}
34
+
35
+	defer root.Close()
36
+
37
+	stat, err := root.Stat()
38
+	if err != nil {
39
+		return
40
+	}
41
+
42
+	if !stat.IsDirectory() {
43
+		return nil, fmt.Errorf("No directory found at %q.", directory)
44
+	}
45
+
46
+	file := filepath.Join(directory, "nodule.yaml")
47
+
48
+	fileobj, err := os.Open(file)
49
+	if err == nil {
50
+		fileobj.Close()
51
+		return []*Nodule{&Nodule{Name: stat.Name, Path: directory}}, nil
52
+	}
53
+
54
+	nodules = make([]*Nodule, 0)
55
+
56
+	for {
57
+		items, err := root.Readdirnames(100)
58
+		if len(items) == 0 {
59
+			if err != nil && err != os.EOF {
60
+				return nil, err
61
+			}
62
+			break
63
+		}
64
+		for _, name := range items {
65
+			path := filepath.Join(directory, name)
66
+			stat, err := os.Stat(path)
67
+			if err != nil {
68
+				return nil, err
69
+			}
70
+			if stat.IsDirectory() {
71
+				file := filepath.Join(path, "nodule.yaml")
72
+				fileobj, err = os.Open(file)
73
+				if err == nil {
74
+					fileobj.Close()
75
+					nodules = append(nodules, &Nodule{Name: name, Path: path})
76
+				}
77
+			}
78
+		}
79
+	}
80
+
81
+	return nodules, nil
82
+
83
+}
84
+
85
+type ConfigEnv struct {
86
+	Profile  string
87
+	Platform string
88
+	Darwin   bool
89
+	Linux    bool
90
+	FreeBSD  bool
91
+}
92
+
93
+var defaultEnv *ConfigEnv
94
+
95
+func GetConfigEnv() *ConfigEnv {
96
+	if defaultEnv != nil {
97
+		return defaultEnv
98
+	}
99
+	env := &ConfigEnv{
100
+		Profile:  runtime.Profile,
101
+		Platform: runtime.Platform,
102
+	}
103
+	switch runtime.Platform {
104
+	case "linux":
105
+		env.Linux = true
106
+	case "freebsd":
107
+		env.FreeBSD = true
108
+	case "darwin":
109
+		env.Darwin = true
110
+	}
111
+	defaultEnv = env
112
+	return env
113
+}
114
+
115
+func EvalStrings(name string, list []string, data interface{}) ([]string, os.Error) {
116
+	result := make([]string, len(list))
117
+	for idx, value := range list {
118
+		if strings.IndexRune(value, '{') == -1 {
119
+			result[idx] = value
120
+		} else {
121
+			tpl := template.New(name)
122
+			buf := &bytes.Buffer{}
123
+			err := tpl.Parse(value)
124
+			if err != nil {
125
+				return nil, err
126
+			}
127
+			err = tpl.Execute(buf, data)
128
+			if err != nil {
129
+				return nil, err
130
+			}
131
+			result[idx] = buf.String()
132
+		}
133
+	}
134
+	return result, nil
135
+}
136
+
137
+type Config struct {
138
+	Type    string
139
+	Build   []string
140
+	Run     []string
141
+	Test    []string
142
+	Depends []string
143
+}
144
+
145
+type Nodule struct {
146
+	Name string
147
+	Path string
148
+	Conf *Config
149
+}
150
+
151
+func (nodule *Nodule) Error(process string, error os.Error) os.Error {
152
+	logging.ErrorData(
153
+		"node",
154
+		fmt.Sprintf("Error %s the %s nodule: %s", process, nodule.Name, error))
155
+	return error
156
+}
157
+
158
+func (nodule *Nodule) ConfigError(msg string, v ...interface{}) os.Error {
159
+	err := fmt.Errorf(msg, v...)
160
+	return nodule.Error("loading config for", err)
161
+}
162
+
163
+func (nodule *Nodule) LoadConf() (err os.Error) {
164
+
165
+	filename := filepath.Join(nodule.Path, "nodule.yaml")
166
+	conf, err := yaml.ParseFile(filename)
167
+	if err != nil {
168
+		return nodule.ConfigError(err.String())
169
+	}
170
+
171
+	typ, ok := conf.GetString("type")
172
+	if !ok {
173
+		return nodule.ConfigError("Couldn't get a config value for 'type' in %s", filename)
174
+	}
175
+
176
+	run, ok := conf.GetStringList("run")
177
+	if !ok {
178
+		switch typ {
179
+		case "go":
180
+			run = []string{"./" + nodule.Name, "{{$.Profile}}.yaml"}
181
+		default:
182
+			return nodule.ConfigError("Couldn't get a config value for 'run' in %s", filename)
183
+		}
184
+	}
185
+
186
+	if len(run) == 0 {
187
+		return nodule.ConfigError("Couldn't get a config value for 'run' in %s", filename)
188
+	}
189
+
190
+	env := GetConfigEnv()
191
+	run, err = EvalStrings("run", run, env)
192
+	if err != nil {
193
+		return nodule.ConfigError(err.String())
194
+	}
195
+
196
+	build, ok := conf.GetStringList("build")
197
+	if !ok {
198
+		switch typ {
199
+		case "go":
200
+			build = []string{"{{if $.FreeBSD}}gmake{{else}}make{{end}}"}
201
+		default:
202
+			return nodule.ConfigError("Couldn't get a config value for 'build' in %s", filename)
203
+		}
204
+	}
205
+
206
+	if len(build) == 0 {
207
+		return nodule.ConfigError("Couldn't get a config value for 'build' in %s", filename)
208
+	}
209
+
210
+	build, err = EvalStrings("build", build, env)
211
+	if err != nil {
212
+		return nodule.ConfigError(err.String())
213
+	}
214
+
215
+	test, ok := conf.GetStringList("test")
216
+	if !ok {
217
+		switch typ {
218
+		case "go":
219
+			test = []string{"gotest", "-v"}
220
+		default:
221
+			return nodule.ConfigError("Couldn't get a config value for 'test' in %s", filename)
222
+		}
223
+	}
224
+
225
+	if len(test) == 0 {
226
+		return nodule.ConfigError("Couldn't get a config value for 'test' in %s", filename)
227
+	}
228
+
229
+	test, err = EvalStrings("test", test, env)
230
+	if err != nil {
231
+		return nodule.ConfigError(err.String())
232
+	}
233
+
234
+	depends, ok := conf.GetStringList("depends")
235
+	if !ok {
236
+		switch typ {
237
+		case "go":
238
+			depends = []string{"*.go"}
239
+		default:
240
+			depends = make([]string, 0)
241
+		}
242
+	}
243
+
244
+	nodule.Conf = &Config{
245
+		Type:    typ,
246
+		Build:   build,
247
+		Run:     run,
248
+		Test:    test,
249
+		Depends: depends,
250
+	}
251
+
252
+	return
253
+
254
+}
255
+
256
+func (nodule *Nodule) handleCommon(process string, args []string) (err os.Error) {
257
+	exe, err := exec.LookPath(args[0])
258
+	if err != nil {
259
+		return nodule.Error(process, err)
260
+	}
261
+	cmd := &exec.Cmd{
262
+		Path: exe,
263
+		Args: args,
264
+		Dir:  nodule.Path,
265
+	}
266
+	output, err := cmd.CombinedOutput()
267
+	if err != nil {
268
+		logging.ErrorData("node", fmt.Errorf("Error running %v: %s", args, err))
269
+		logging.ErrorData("node", "\n\n"+string(output))
270
+		return
271
+	}
272
+	return
273
+}
274
+
275
+func (nodule *Nodule) Build() (err os.Error) {
276
+	Log("Building the %s nodule: %s", nodule.Name, nodule.Path)
277
+	if nodule.Conf == nil {
278
+		err = nodule.LoadConf()
279
+		if err != nil {
280
+			return
281
+		}
282
+	}
283
+	return nodule.handleCommon("building", nodule.Conf.Build)
284
+}
285
+
286
+func (nodule *Nodule) Test() (err os.Error) {
287
+	err = nodule.Build()
288
+	if err != nil {
289
+		return
290
+	}
291
+	Log("Testing the %s nodule: %s", nodule.Name, nodule.Path)
292
+	return nodule.handleCommon("testing", nodule.Conf.Test)
293
+}
294
+
295
+func (nodule *Nodule) Review() (err os.Error) {
296
+	err = nodule.Test()
297
+	if err != nil {
298
+		return
299
+	}
300
+	Log("Reviewing the %s nodule: %s", nodule.Name, nodule.Path)
301
+	return
302
+}
303
+
304
+func (nodule *Nodule) Run() (err os.Error) {
305
+	Log("Running the %s nodule: %s", nodule.Name, nodule.Path)
306
+	return nodule.handleCommon("running", nodule.Conf.Run)
307
+}
308
+
309
+type Host struct {
310
+	CachePath    string
311
+	ControlTCP   net.Listener
312
+	ControlUnix  net.Listener
313
+	LastRef      uint64
314
+	Listener     net.Listener
315
+	Nodules      map[uint64]*Nodule
316
+	ReadTimeout  int64
317
+	WriteTimeout int64
318
+}
319
+
320
+func (host *Host) Run(debug bool) (err os.Error) {
321
+	for {
322
+
323
+	}
324
+	return
325
+}
326
+
327
+func NewHost(runPath, hostAddress string, hostPort int, ctrlAddress string, ctrlPort int, initNodules string, nodulePaths []string, master *master.Client) (host *Host, err os.Error) {
328
+
329
+	// Create the cache directory if it doesn't exist.
330
+	cachePath := filepath.Join(runPath, "cache")
331
+	err = os.MkdirAll(cachePath, 0755)
332
+	if err != nil {
333
+		return
334
+	}
335
+
336
+	ctrlTCP, err := net.Listen("tcp", fmt.Sprintf("%s:%d", ctrlAddress, ctrlPort))
337
+	if err != nil {
338
+		return
339
+	}
340
+
341
+	socket := filepath.Join(runPath, "node.sock")
342
+	_, err = os.Stat(socket)
343
+	if err == nil {
344
+		err = os.Remove(socket)
345
+		if err != nil {
346
+			return
347
+		}
348
+	}
349
+
350
+	ctrlUnix, err := net.Listen("unix", socket)
351
+	if err != nil {
352
+		return
353
+	}
354
+
355
+	listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", hostAddress, hostPort))
356
+	if err != nil {
357
+		return
358
+	}
359
+
360
+	host = &Host{
361
+		CachePath:    cachePath,
362
+		ControlTCP:   ctrlTCP,
363
+		ControlUnix:  ctrlUnix,
364
+		Listener:     listener,
365
+		ReadTimeout:  60 * 1e9,
366
+		WriteTimeout: 60 * 1e9,
367
+	}
368
+
369
+	return
370
+
371
+}
372
+
373
+func Log(msg string, args ...interface{}) {
374
+	if len(args) > 0 {
375
+		logging.InfoData("node", fmt.Sprintf(msg, args...))
376
+	} else {
377
+		logging.InfoData("node", msg)
378
+	}
379
+}
380
+
381
+func FilterConsoleLog(record *logging.Record) (write bool, data []interface{}) {
382
+	if len(record.Items) > 0 {
383
+		meta := record.Items[0]
384
+		switch meta.(type) {
385
+		case string:
386
+			if meta.(string) == "node" {
387
+				return true, record.Items[1:]
388
+			}
389
+		}
390
+	}
391
+	return true, nil
392
+}