/
def.go
167 lines (145 loc) · 3.4 KB
/
def.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package def
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"gopkg.in/yaml.v1"
)
// ServiceWithResolvedDeps ...
type ServiceWithResolvedDeps struct {
Service
Deps []ServiceWithResolvedDeps `yaml:"-"`
}
// Service ...
type Service struct {
Name string `yaml:"name"`
Source string `yaml:"source"`
Version string `yaml:"version"`
Targets map[string]string `yaml:"targets"`
Tags []string `yaml:"tags"`
Config []Config `yaml:"config"`
Deps []Dep `yaml:"deps"`
}
// Config ...
type Config struct {
Desc string `yaml:"desc"`
Env string `yaml:"env"`
Flag string `yaml:"flag"`
Type string `yaml:"type"`
}
// Dep ...
type Dep struct {
Source string `yaml:"source"`
Version string `yaml:"version"`
}
// DefaultNames is a list of default service.yml filenames
var DefaultNames = []string{
"service.yml", "service.json", ".service.yml", ".service.json",
"svc.yml", "svc.json", ".svc.yml", ".svc.json",
}
// Load loads the service definition file
func Load(filename string) (svc Service, err error) {
var b []byte
b, err = ioutil.ReadFile(filename)
if err != nil {
return
}
svc, err = LoadBytes(filename, b)
return
}
// LoadBytes loads the service definition bytes
func LoadBytes(filename string, b []byte) (svc Service, err error) {
switch {
case strings.HasSuffix(filename, ".json"):
err = json.Unmarshal(b, &svc)
case strings.HasSuffix(filename, ".yml"):
fallthrough
case strings.HasSuffix(filename, ".yaml"):
fallthrough
default:
err = yaml.Unmarshal(b, &svc)
}
return
}
// MustLoad loads the service definition file, but panics on error
func MustLoad(filename string) Service {
return MustLoadOne(filename)
}
// LoadOne ...
func LoadOne(filenames ...string) (svc Service, err error) {
for _, f := range filenames {
svc, err = Load(f)
if err == nil {
return
}
}
return
}
// MustLoadOne ...
func MustLoadOne(filenames ...string) Service {
svc, err := LoadOne(filenames...)
if err != nil {
panic(err)
}
return svc
}
// Resolve resolves service dependencies
func (s Service) Resolve() (res *ServiceWithResolvedDeps, err error) {
res = &ServiceWithResolvedDeps{s, nil}
for _, dep := range s.Deps {
name, b, err := ResolveSource(dep.Source)
if err != nil {
return res, err
}
svc, err := LoadBytes(name, b)
if err != nil {
return res, err
}
r, err := svc.Resolve()
if err != nil {
return res, err
}
res.Deps = append(res.Deps, *r)
}
return
}
// ResolveSource loads a source path
func ResolveSource(source string) (name string, b []byte, err error) {
switch {
case strings.HasPrefix(source, "../"):
fallthrough
case strings.HasPrefix(source, "./"):
fallthrough
case strings.HasPrefix(source, "/"):
for _, d := range DefaultNames {
b, err = ioutil.ReadFile(source + "/" + d)
if err == nil {
break
}
}
case strings.HasPrefix(source, "github.com/"):
var res *http.Response
src := strings.TrimPrefix(source, "github.com/")
for _, name = range DefaultNames {
res, err = http.Get("https://raw.githubusercontent.com/" + src + "/master/" + name)
if err != nil {
continue
}
if res.StatusCode > 399 {
err = fmt.Errorf("unexpected status: %d", res.StatusCode)
continue
}
b, err = ioutil.ReadAll(res.Body)
if err != nil {
err = fmt.Errorf("error reading body: %s", err)
continue
}
break
}
default:
err = fmt.Errorf("unrecognised source type: %s", source)
}
return
}