forked from owulveryck/toscalib
-
Notifications
You must be signed in to change notification settings - Fork 4
/
parser.go
201 lines (170 loc) · 5.15 KB
/
parser.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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/*
Copyright 2015 - Olivier Wulveryck
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package toscalib
import (
"archive/zip"
"fmt"
"io"
"io/ioutil"
"path/filepath"
"golang.org/x/tools/godoc/vfs"
"golang.org/x/tools/godoc/vfs/zipfs"
"gopkg.in/yaml.v2"
)
// ParserHooks provide callback functions for handling custom logic at
// key points within the overall parsing logic.
type ParserHooks struct {
ParsedSTD func(source string, std *ServiceTemplateDefinition) error
}
func noop(source string, std *ServiceTemplateDefinition) error {
return nil
}
// ParseCsar handles open and parse the CSAR file
func (t *ServiceTemplateDefinition) ParseCsar(zipfile string) error {
type meta struct {
Version string `yaml:"TOSCA-Meta-File-Version"`
CsarVersion string `yaml:"CSAR-Version"`
CreatedBy string `yaml:"Created-By"`
EntryDefinition string `yaml:"Entry-Definitions"`
}
rc, err := zip.OpenReader(zipfile)
if err != nil {
return err
}
defer rc.Close()
fs := zipfs.New(rc, zipfile)
out, err := vfs.ReadFile(fs, "/TOSCA-Metadata/TOSCA.meta")
if err != nil {
return err
}
var m meta
err = yaml.Unmarshal(out, &m)
if err != nil {
return err
}
dirname := fmt.Sprintf("/%v", filepath.Dir(m.EntryDefinition))
base := filepath.Base(m.EntryDefinition)
ns := vfs.NameSpace{}
ns.Bind("/", fs, dirname, vfs.BindReplace)
// pass in a resolver that has the context of the virtual filespace
// of the archive file to handle resolving imports
return t.ParseSource(base, func(l string) ([]byte, error) {
var r []byte
rsc, err := ns.Open(l)
if err != nil {
return r, err
}
return ioutil.ReadAll(rsc)
}, ParserHooks{ParsedSTD: noop}) // TODO(kenjones): Add hooks as method parameter
}
func parseImports(baseDir string, impDefs []ImportDefinition, resolver Resolver, hooks ParserHooks) (ServiceTemplateDefinition, error) {
var std ServiceTemplateDefinition
for _, im := range impDefs {
imFilePath := im.File
if baseDir != "" {
if temp := filepath.Join(baseDir, imFilePath); isAbsLocalPath(temp) {
imFilePath = temp
}
}
r, err := resolver(imFilePath)
if err != nil {
return std, err
}
var tt ServiceTemplateDefinition
err = yaml.Unmarshal(r, &tt)
if err != nil {
return std, err
}
err = hooks.ParsedSTD(imFilePath, &tt)
if err != nil {
return std, err
}
if len(tt.Imports) != 0 {
var imptt ServiceTemplateDefinition
imptt, err = parseImports(baseDir, tt.Imports, resolver, hooks)
if err != nil {
return std, err
}
tt = tt.Merge(imptt)
}
std = std.Merge(tt)
}
return std, nil
}
func (t *ServiceTemplateDefinition) parse(baseDir string, data []byte, resolver Resolver, hooks ParserHooks) error {
var std ServiceTemplateDefinition
// Unmarshal the data in an interface
err := yaml.Unmarshal(data, &std)
if err != nil {
return err
}
err = hooks.ParsedSTD("", &std)
if err != nil {
return err
}
// Import the normative types by default
for _, normType := range AssetNames() {
// the normType comes from the defined list so this will
// always be successful, if not then panic is the correct
// approach for this kind of parsing.
data := MustAsset(normType)
var tt ServiceTemplateDefinition
err = yaml.Unmarshal(data, &tt)
if err != nil {
return err
}
err = hooks.ParsedSTD(normType, &tt)
if err != nil {
return err
}
std = std.Merge(tt)
}
// Load all referenced Imports (recursively)
var tt ServiceTemplateDefinition
tt, err = parseImports(baseDir, std.Imports, resolver, hooks)
if err != nil {
return err
}
std = std.Merge(tt)
// update the initial context with the freshly loaded context
*t = std
// resolve all references and inherited elements
t.resolve()
return nil
}
// ParseReader retrieves and parses a TOSCA document and loads into the structure using
// specified Resolver function to retrieve remote imports.
func (t *ServiceTemplateDefinition) ParseReader(r io.Reader, resolver Resolver, hooks ParserHooks) error {
data, err := ioutil.ReadAll(r)
if err != nil {
return err
}
return t.parse("", data, resolver, hooks)
}
// ParseSource retrieves and parses a TOSCA document and loads into the structure using
// specified Resolver function to retrieve remote source or imports.
func (t *ServiceTemplateDefinition) ParseSource(source string, resolver Resolver, hooks ParserHooks) error {
baseDir := ""
if isAbsLocalPath(source) {
baseDir, _ = filepath.Split(source)
}
data, err := resolver(source)
if err != nil {
return err
}
return t.parse(baseDir, data, resolver, hooks)
}
// Parse a TOSCA document and fill in the structure
func (t *ServiceTemplateDefinition) Parse(r io.Reader) error {
return t.ParseReader(r, defaultResolver, ParserHooks{ParsedSTD: noop})
}