Browse Source

Provide OpenAPI spec for Blubberoid

Wrote an OpenAPI 3.0 spec for Blubberoid that provides `x-amples`
entries compatible with service-checker.

The written spec includes basic schema for Blubber config objects that
may be later factored out for use in validation.

Note that OpenAPI 3.0 supports only the v4 draft of the JSON Schema
standard, so some parts of the configuration could not be fully
described. Specifically, v4 does not include the `patternProperties`
definition introduced in the JSON Schema v6 draft that would allow us to
describe `variants` and `runs.environment` and everything beneath.

Blubberoid was refactored slightly to incorporate the new spec as well
as assume JSON as the canonical and default configuration format. It was
also refactored to include a versioned namespace ("v1") after the server
endpoint.

Bug: T205920
Change-Id: I28a341aa503b8920d802715660d4c4d62be45475
Dan Duvall 1 year ago
parent
commit
4df63a83c9

+ 6
- 2
Makefile View File

@@ -16,10 +16,11 @@ GO_LDFLAGS := \
16 16
 #
17 17
 # workaround bug in case CURDIR is a symlink
18 18
 # see https://github.com/golang/go/issues/24359
19
+GO_GENERATE := cd "$(REAL_CURDIR)" && go generate
19 20
 GO_BUILD := cd "$(REAL_CURDIR)" && go build -v -ldflags "$(GO_LDFLAGS)"
20 21
 GO_INSTALL := cd "$(REAL_CURDIR)" && go install -v -ldflags "$(GO_LDFLAGS)"
21 22
 
22
-all: blubber blubberoid
23
+all: code blubber blubberoid
23 24
 
24 25
 blubber:
25 26
 	$(GO_BUILD) ./cmd/blubber
@@ -27,11 +28,14 @@ blubber:
27 28
 blubberoid:
28 29
 	$(GO_BUILD) ./cmd/blubberoid
29 30
 
31
+code:
32
+	$(GO_GENERATE) $(GO_PACKAGES)
33
+
30 34
 clean:
31 35
 	go clean
32 36
 	rm -f blubber blubberoid
33 37
 
34
-install:
38
+install: all
35 39
 	$(GO_INSTALL) $(GO_PACKAGES)
36 40
 
37 41
 release:

+ 217
- 0
api/openapi-spec/blubberoid.yaml View File

@@ -0,0 +1,217 @@
1
+---
2
+openapi: '3.0.0'
3
+info:
4
+  title: Blubberoid
5
+  description: >
6
+    Blubber is a highly opinionated abstraction for container build
7
+    configurations.
8
+  version: {{ .Version }}
9
+paths:
10
+  /v1/{variant}:
11
+    post:
12
+      summary: >
13
+        Generates a valid Dockerfile based on Blubber YAML configuration
14
+        provided in the request body and the given variant name.
15
+      requestBody:
16
+        description: A valid Blubber configuration.
17
+        required: true
18
+        content:
19
+          application/json:
20
+            schema:
21
+              $ref: '#/components/schemas/v3.Config'
22
+          application/yaml:
23
+            schema:
24
+              type: string
25
+          application/x-yaml:
26
+            schema:
27
+              type: string
28
+      responses:
29
+        '200':
30
+          description: OK. Response body should be a valid Dockerfile.
31
+          content:
32
+            text/plain:
33
+              schema:
34
+                type: string
35
+        '400':
36
+          description: Bad request. The YAML request body failed to parse.
37
+        '404':
38
+          description: No variant name was provided in the request path.
39
+        '422':
40
+          description: Provided Blubber config parsed correctly but failed validation.
41
+        '5XX':
42
+          description: An unexpected service-side error.
43
+
44
+      x-amples:
45
+        - title: Mathoid test variant
46
+          request:
47
+            params:
48
+              variant: test
49
+            headers:
50
+              Content-Type: application/json
51
+            body: {
52
+              "version": "v3",
53
+              "base": "docker-registry.wikimedia.org/nodejs-slim",
54
+              "apt": { "packages": ["librsvg2-2"] },
55
+              "lives": { "in": "/srv/service" },
56
+              "variants": {
57
+                "build": {
58
+                  "base": "docker-registry.wikimedia.org/nodejs-devel",
59
+                  "apt": {
60
+                    "packages": ["librsvg2-dev", "git", "pkg-config", "build-essential"]
61
+                  },
62
+                  "node": { "requirements": ["package.json"] },
63
+                  "runs": { "environment": { "LINK": "g++" } }
64
+                },
65
+                "test": { "includes": ["build"], "entrypoint": ["npm", "test"] }
66
+              }
67
+            }
68
+          response:
69
+            status: 200
70
+            headers:
71
+              content-type: text/plain
72
+            body: /^FROM docker-registry.wikimedia.org\/nodejs-devel/
73
+
74
+components:
75
+  schemas:
76
+    v3.Config:
77
+      title: Top-level blubber configuration (version v3)
78
+      allOf:
79
+        - $ref: '#/components/schemas/v3.CommonConfig'
80
+        - type: object
81
+          properties:
82
+            required: [version, variants]
83
+            version:
84
+              type: string
85
+              description: Blubber configuration version
86
+            variants:
87
+              type: object
88
+              description: Configuration variants (e.g. development, test, production)
89
+              additionalProperties: true
90
+              # OpenAPI 3.0 supports only v4 of the JSON Schema draft spec and
91
+              # cannot define schema for object properties with arbitrary
92
+              # names, but the following commented section is included to be
93
+              # useful to humans for the time being. It patiently awaits v6
94
+              # json schema draft support before its uncommenting.
95
+              #
96
+              # patternProperties:
97
+              #   "^[a-zA-Z][a-zA-Z0-9\-\.]+[a-zA-Z0-9]$":
98
+              #     $ref: '#/components/schemas/v3.VariantConfig'
99
+
100
+    v3.CommonConfig:
101
+      type: object
102
+      properties:
103
+        base:
104
+          type: string
105
+          description: Base image reference
106
+        apt:
107
+          type: object
108
+          properties:
109
+            packages:
110
+              type: array
111
+              description: Packages to install from APT sources of base image
112
+              items:
113
+                type: string
114
+        node:
115
+          type: object
116
+          properties:
117
+            env:
118
+              type: string
119
+              description: Node environment (e.g. production, etc.)
120
+            requirements:
121
+              type: array
122
+              description: Files needed for Node package installation (e.g. package.json, package-lock.json)
123
+              items:
124
+                type: string
125
+        python:
126
+          type: object
127
+          properties:
128
+            version:
129
+              type: string
130
+              description: Python binary present in the system (e.g. python3)
131
+            requirements:
132
+              type: array
133
+              description: Files needed for Python package installation (e.g. requirements.txt, etc.)
134
+              items:
135
+                type: string
136
+        builder:
137
+          type: object
138
+          properties:
139
+            command:
140
+              type: array
141
+              description: Command and arguments of an arbitrary build command
142
+              items:
143
+                type: string
144
+            requirements:
145
+              type: array
146
+              description: Files needed by the build command (e.g. Makefile, ./src/, etc.)
147
+              items:
148
+                type: string
149
+        lives:
150
+          type: object
151
+          properties:
152
+            as:
153
+              type: string
154
+              description: Owner (name) of application files within the container
155
+            uid:
156
+              type: integer
157
+              description: Owner (UID) of application files within the container
158
+            gid:
159
+              type: integer
160
+              description: Group owner (GID) of application files within the container
161
+            in:
162
+              type: string
163
+              description: Application working directory within the container
164
+        runs:
165
+          type: object
166
+          properties:
167
+            as:
168
+              type: string
169
+              description: Runtime process owner (name) of application entrypoint
170
+            uid:
171
+              type: integer
172
+              description: Runtime process owner (UID) of application entrypoint
173
+            gid:
174
+              type: integer
175
+              description: Runtime process group (GID) of application entrypoint
176
+            environment:
177
+              type: object
178
+              description: Environment variables and values to be set before entrypoint execution
179
+              additionalProperties: true
180
+            insecurely:
181
+              type: boolean
182
+              description: Skip dropping of priviledge to the runtime process owner before entrypoint execution
183
+        entrypoint:
184
+          type: array
185
+          description: Runtime entry point command and arguments
186
+          items:
187
+            type: string
188
+    v3.VariantConfig:
189
+      allOf:
190
+        - $ref: '#/components/schemas/v3.CommonConfig'
191
+        - type: object
192
+          properties:
193
+            includes:
194
+              type: array
195
+              description: Names of other variants to inherit configuration from
196
+              items:
197
+                description: Variant name
198
+                type: string
199
+            copies:
200
+              type: string
201
+              description: Name of variant from which to copy application files, resulting in a multi-stage build
202
+            artifacts:
203
+              type: array
204
+              items:
205
+                type: object
206
+                description: Artifacts to copy from another variant, resulting in a multi-stage build
207
+                required: [from, source, destination]
208
+                properties:
209
+                  from:
210
+                    type: string
211
+                    description: Variant name
212
+                  source:
213
+                    type: string
214
+                    description: Path of files/directories to copy
215
+                  destination:
216
+                    type: string
217
+                    description: Destination path

+ 67
- 23
cmd/blubberoid/main.go View File

@@ -3,27 +3,32 @@
3 3
 package main
4 4
 
5 5
 import (
6
-	"encoding/json"
6
+	"bytes"
7 7
 	"fmt"
8 8
 	"io/ioutil"
9 9
 	"log"
10 10
 	"mime"
11 11
 	"net/http"
12
+	"net/url"
12 13
 	"os"
13 14
 	"path"
15
+	"strings"
16
+	"text/template"
14 17
 
15 18
 	"github.com/pborman/getopt/v2"
16 19
 
17 20
 	"gerrit.wikimedia.org/r/blubber/config"
18 21
 	"gerrit.wikimedia.org/r/blubber/docker"
22
+	"gerrit.wikimedia.org/r/blubber/meta"
19 23
 )
20 24
 
21 25
 var (
22
-	showHelp  = getopt.BoolLong("help", 'h', "show help/usage")
23
-	address   = getopt.StringLong("address", 'a', ":8748", "socket address/port to listen on (default ':8748')", "address:port")
24
-	endpoint  = getopt.StringLong("endpoint", 'e', "/", "server endpoint (default '/')", "path")
25
-	policyURI = getopt.StringLong("policy", 'p', "", "policy file URI", "uri")
26
-	policy    *config.Policy
26
+	showHelp    = getopt.BoolLong("help", 'h', "show help/usage")
27
+	address     = getopt.StringLong("address", 'a', ":8748", "socket address/port to listen on (default ':8748')", "address:port")
28
+	endpoint    = getopt.StringLong("endpoint", 'e', "/", "server endpoint (default '/')", "path")
29
+	policyURI   = getopt.StringLong("policy", 'p', "", "policy file URI", "uri")
30
+	policy      *config.Policy
31
+	openAPISpec []byte
27 32
 )
28 33
 
29 34
 func main() {
@@ -51,7 +56,10 @@ func main() {
51 56
 		*endpoint += "/"
52 57
 	}
53 58
 
54
-	log.Printf("listening on %s for requests to %s[variant]\n", *address, *endpoint)
59
+	// Evaluate OpenAPI spec template and store results for ?spec requests
60
+	openAPISpec = readOpenAPISpec()
61
+
62
+	log.Printf("listening on %s for requests to %sv1/[variant]\n", *address, *endpoint)
55 63
 
56 64
 	http.HandleFunc(*endpoint, blubberoid)
57 65
 	log.Fatal(http.ListenAndServe(*address, nil))
@@ -59,12 +67,35 @@ func main() {
59 67
 
60 68
 func blubberoid(res http.ResponseWriter, req *http.Request) {
61 69
 	if len(req.URL.Path) <= len(*endpoint) {
70
+		if req.URL.RawQuery == "spec" {
71
+			res.Header().Set("Content-Type", "text/plain")
72
+			res.Write(openAPISpec)
73
+			return
74
+		}
75
+
62 76
 		res.WriteHeader(http.StatusNotFound)
63
-		res.Write(responseBody("request a variant at %s[variant]", *endpoint))
77
+		res.Write(responseBody("request a variant at %sv1/[variant]", *endpoint))
78
+		return
79
+	}
80
+
81
+	requestPath := req.URL.Path[len(*endpoint):]
82
+	pathSegments := strings.Split(requestPath, "/")
83
+
84
+	// Request should have been to v1/[variant]
85
+	if len(pathSegments) != 2 || pathSegments[0] != "v1" {
86
+		res.WriteHeader(http.StatusNotFound)
87
+		res.Write(responseBody("request a variant at %sv1/[variant]", *endpoint))
88
+		return
89
+	}
90
+
91
+	variant, err := url.PathUnescape(pathSegments[1])
92
+
93
+	if err != nil {
94
+		res.WriteHeader(http.StatusInternalServerError)
95
+		log.Printf("failed to unescape variant name '%s': %s\n", pathSegments[1], err)
64 96
 		return
65 97
 	}
66 98
 
67
-	variant := req.URL.Path[len(*endpoint):]
68 99
 	body, err := ioutil.ReadAll(req.Body)
69 100
 
70 101
 	if err != nil {
@@ -73,25 +104,25 @@ func blubberoid(res http.ResponseWriter, req *http.Request) {
73 104
 		return
74 105
 	}
75 106
 
76
-	switch mt, _, _ := mime.ParseMediaType(req.Header.Get("content-type")); mt {
107
+	var cfg *config.Config
108
+	mediaType, _, _ := mime.ParseMediaType(req.Header.Get("content-type"))
109
+
110
+	// Default to application/json
111
+	if mediaType == "" {
112
+		mediaType = "application/json"
113
+	}
114
+
115
+	switch mediaType {
77 116
 	case "application/json":
78
-		// Enforce strict JSON syntax if specified, even though the config parser
79
-		// would technically handle anything that's at least valid YAML
80
-		if !json.Valid(body) {
81
-			res.WriteHeader(http.StatusBadRequest)
82
-			res.Write(responseBody("'%s' media type given but request contains invalid JSON", mt))
83
-			return
84
-		}
117
+		cfg, err = config.ReadConfig(body)
85 118
 	case "application/yaml", "application/x-yaml":
86
-		// Let the config parser validate YAML syntax
119
+		cfg, err = config.ReadYAMLConfig(body)
87 120
 	default:
88 121
 		res.WriteHeader(http.StatusUnsupportedMediaType)
89
-		res.Write(responseBody("'%s' media type is not supported", mt))
122
+		res.Write(responseBody("'%s' media type is not supported", mediaType))
90 123
 		return
91 124
 	}
92 125
 
93
-	cfg, err := config.ReadYAMLConfig(body)
94
-
95 126
 	if err != nil {
96 127
 		if config.IsValidationError(err) {
97 128
 			res.WriteHeader(http.StatusUnprocessableEntity)
@@ -101,8 +132,8 @@ func blubberoid(res http.ResponseWriter, req *http.Request) {
101 132
 
102 133
 		res.WriteHeader(http.StatusBadRequest)
103 134
 		res.Write(responseBody(
104
-			"Failed to read config YAML from request body. "+
105
-				"Was it formatted correctly and encoded as binary data?\nerror: %s",
135
+			"Failed to read '%s' config from request body. Error: %s",
136
+			mediaType,
106 137
 			err.Error(),
107 138
 		))
108 139
 		return
@@ -136,3 +167,16 @@ func blubberoid(res http.ResponseWriter, req *http.Request) {
136 167
 func responseBody(msg string, a ...interface{}) []byte {
137 168
 	return []byte(fmt.Sprintf(msg+"\n", a...))
138 169
 }
170
+
171
+func readOpenAPISpec() []byte {
172
+	var buffer bytes.Buffer
173
+	tmpl, _ := template.New("spec").Parse(openAPISpecTemplate)
174
+
175
+	tmpl.Execute(&buffer, struct {
176
+		Version string
177
+	}{
178
+		Version: meta.FullVersion(),
179
+	})
180
+
181
+	return buffer.Bytes()
182
+}

+ 5
- 5
cmd/blubberoid/main_test.go View File

@@ -12,7 +12,7 @@ import (
12 12
 
13 13
 func TestBlubberoidYAMLRequest(t *testing.T) {
14 14
 	rec := httptest.NewRecorder()
15
-	req := httptest.NewRequest("POST", "/test", strings.NewReader(`---
15
+	req := httptest.NewRequest("POST", "/v1/test", strings.NewReader(`---
16 16
     version: v3
17 17
     base: foo
18 18
     variants:
@@ -33,7 +33,7 @@ func TestBlubberoidYAMLRequest(t *testing.T) {
33 33
 func TestBlubberoidJSONRequest(t *testing.T) {
34 34
 	t.Run("valid JSON syntax", func(t *testing.T) {
35 35
 		rec := httptest.NewRecorder()
36
-		req := httptest.NewRequest("POST", "/test", strings.NewReader(`{
36
+		req := httptest.NewRequest("POST", "/v1/test", strings.NewReader(`{
37 37
 			"version": "v3",
38 38
 			"base": "foo",
39 39
 			"variants": {
@@ -55,7 +55,7 @@ func TestBlubberoidJSONRequest(t *testing.T) {
55 55
 
56 56
 	t.Run("invalid JSON syntax", func(t *testing.T) {
57 57
 		rec := httptest.NewRecorder()
58
-		req := httptest.NewRequest("POST", "/test", strings.NewReader(`{
58
+		req := httptest.NewRequest("POST", "/v1/test", strings.NewReader(`{
59 59
 			version: "v3",
60 60
 			base: "foo",
61 61
 			variants: {
@@ -70,13 +70,13 @@ func TestBlubberoidJSONRequest(t *testing.T) {
70 70
 		body, _ := ioutil.ReadAll(resp.Body)
71 71
 
72 72
 		assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
73
-		assert.Equal(t, string(body), "'application/json' media type given but request contains invalid JSON\n")
73
+		assert.Equal(t, string(body), "Failed to read 'application/json' config from request body. Error: invalid character 'v' looking for beginning of object key string\n")
74 74
 	})
75 75
 }
76 76
 
77 77
 func TestBlubberoidUnsupportedMediaType(t *testing.T) {
78 78
 	rec := httptest.NewRecorder()
79
-	req := httptest.NewRequest("POST", "/test", strings.NewReader(``))
79
+	req := httptest.NewRequest("POST", "/v1/test", strings.NewReader(``))
80 80
 	req.Header.Set("Content-Type", "application/foo")
81 81
 
82 82
 	blubberoid(rec, req)

+ 222
- 0
cmd/blubberoid/openapi.go View File

@@ -0,0 +1,222 @@
1
+// Code generated by ../../scripts/generate-const.sh DO NOT EDIT.
2
+package main
3
+
4
+//go:generate ../../scripts/generate-const.sh openAPISpecTemplate ../../api/openapi-spec/blubberoid.yaml
5
+const openAPISpecTemplate = `---
6
+openapi: '3.0.0'
7
+info:
8
+  title: Blubberoid
9
+  description: >
10
+    Blubber is a highly opinionated abstraction for container build
11
+    configurations.
12
+  version: {{ .Version }}
13
+paths:
14
+  /v1/{variant}:
15
+    post:
16
+      summary: >
17
+        Generates a valid Dockerfile based on Blubber YAML configuration
18
+        provided in the request body and the given variant name.
19
+      requestBody:
20
+        description: A valid Blubber configuration.
21
+        required: true
22
+        content:
23
+          application/json:
24
+            schema:
25
+              $ref: '#/components/schemas/v3.Config'
26
+          application/yaml:
27
+            schema:
28
+              type: string
29
+          application/x-yaml:
30
+            schema:
31
+              type: string
32
+      responses:
33
+        '200':
34
+          description: OK. Response body should be a valid Dockerfile.
35
+          content:
36
+            text/plain:
37
+              schema:
38
+                type: string
39
+        '400':
40
+          description: Bad request. The YAML request body failed to parse.
41
+        '404':
42
+          description: No variant name was provided in the request path.
43
+        '422':
44
+          description: Provided Blubber config parsed correctly but failed validation.
45
+        '5XX':
46
+          description: An unexpected service-side error.
47
+
48
+      x-amples:
49
+        - title: Mathoid test variant
50
+          request:
51
+            params:
52
+              variant: test
53
+            headers:
54
+              Content-Type: application/json
55
+            body: {
56
+              "version": "v3",
57
+              "base": "docker-registry.wikimedia.org/nodejs-slim",
58
+              "apt": { "packages": ["librsvg2-2"] },
59
+              "lives": { "in": "/srv/service" },
60
+              "variants": {
61
+                "build": {
62
+                  "base": "docker-registry.wikimedia.org/nodejs-devel",
63
+                  "apt": {
64
+                    "packages": ["librsvg2-dev", "git", "pkg-config", "build-essential"]
65
+                  },
66
+                  "node": { "requirements": ["package.json"] },
67
+                  "runs": { "environment": { "LINK": "g++" } }
68
+                },
69
+                "test": { "includes": ["build"], "entrypoint": ["npm", "test"] }
70
+              }
71
+            }
72
+          response:
73
+            status: 200
74
+            headers:
75
+              content-type: text/plain
76
+            body: /^FROM docker-registry.wikimedia.org\/nodejs-devel/
77
+
78
+components:
79
+  schemas:
80
+    v3.Config:
81
+      title: Top-level blubber configuration (version v3)
82
+      allOf:
83
+        - $ref: '#/components/schemas/v3.CommonConfig'
84
+        - type: object
85
+          properties:
86
+            required: [version, variants]
87
+            version:
88
+              type: string
89
+              description: Blubber configuration version
90
+            variants:
91
+              type: object
92
+              description: Configuration variants (e.g. development, test, production)
93
+              additionalProperties: true
94
+              # OpenAPI 3.0 supports only v4 of the JSON Schema draft spec and
95
+              # cannot define schema for object properties with arbitrary
96
+              # names, but the following commented section is included to be
97
+              # useful to humans for the time being. It patiently awaits v6
98
+              # json schema draft support before its uncommenting.
99
+              #
100
+              # patternProperties:
101
+              #   "^[a-zA-Z][a-zA-Z0-9\-\.]+[a-zA-Z0-9]$":
102
+              #     $ref: '#/components/schemas/v3.VariantConfig'
103
+
104
+    v3.CommonConfig:
105
+      type: object
106
+      properties:
107
+        base:
108
+          type: string
109
+          description: Base image reference
110
+        apt:
111
+          type: object
112
+          properties:
113
+            packages:
114
+              type: array
115
+              description: Packages to install from APT sources of base image
116
+              items:
117
+                type: string
118
+        node:
119
+          type: object
120
+          properties:
121
+            env:
122
+              type: string
123
+              description: Node environment (e.g. production, etc.)
124
+            requirements:
125
+              type: array
126
+              description: Files needed for Node package installation (e.g. package.json, package-lock.json)
127
+              items:
128
+                type: string
129
+        python:
130
+          type: object
131
+          properties:
132
+            version:
133
+              type: string
134
+              description: Python binary present in the system (e.g. python3)
135
+            requirements:
136
+              type: array
137
+              description: Files needed for Python package installation (e.g. requirements.txt, etc.)
138
+              items:
139
+                type: string
140
+        builder:
141
+          type: object
142
+          properties:
143
+            command:
144
+              type: array
145
+              description: Command and arguments of an arbitrary build command
146
+              items:
147
+                type: string
148
+            requirements:
149
+              type: array
150
+              description: Files needed by the build command (e.g. Makefile, ./src/, etc.)
151
+              items:
152
+                type: string
153
+        lives:
154
+          type: object
155
+          properties:
156
+            as:
157
+              type: string
158
+              description: Owner (name) of application files within the container
159
+            uid:
160
+              type: integer
161
+              description: Owner (UID) of application files within the container
162
+            gid:
163
+              type: integer
164
+              description: Group owner (GID) of application files within the container
165
+            in:
166
+              type: string
167
+              description: Application working directory within the container
168
+        runs:
169
+          type: object
170
+          properties:
171
+            as:
172
+              type: string
173
+              description: Runtime process owner (name) of application entrypoint
174
+            uid:
175
+              type: integer
176
+              description: Runtime process owner (UID) of application entrypoint
177
+            gid:
178
+              type: integer
179
+              description: Runtime process group (GID) of application entrypoint
180
+            environment:
181
+              type: object
182
+              description: Environment variables and values to be set before entrypoint execution
183
+              additionalProperties: true
184
+            insecurely:
185
+              type: boolean
186
+              description: Skip dropping of priviledge to the runtime process owner before entrypoint execution
187
+        entrypoint:
188
+          type: array
189
+          description: Runtime entry point command and arguments
190
+          items:
191
+            type: string
192
+    v3.VariantConfig:
193
+      allOf:
194
+        - $ref: '#/components/schemas/v3.CommonConfig'
195
+        - type: object
196
+          properties:
197
+            includes:
198
+              type: array
199
+              description: Names of other variants to inherit configuration from
200
+              items:
201
+                description: Variant name
202
+                type: string
203
+            copies:
204
+              type: string
205
+              description: Name of variant from which to copy application files, resulting in a multi-stage build
206
+            artifacts:
207
+              type: array
208
+              items:
209
+                type: object
210
+                description: Artifacts to copy from another variant, resulting in a multi-stage build
211
+                required: [from, source, destination]
212
+                properties:
213
+                  from:
214
+                    type: string
215
+                    description: Variant name
216
+                  source:
217
+                    type: string
218
+                    description: Path of files/directories to copy
219
+                  destination:
220
+                    type: string
221
+                    description: Destination path
222
+`

+ 16
- 0
cmd/blubberoid/openapi_test.go View File

@@ -0,0 +1,16 @@
1
+package main
2
+
3
+import (
4
+	"io/ioutil"
5
+	"testing"
6
+
7
+	"github.com/stretchr/testify/assert"
8
+)
9
+
10
+func TestBlubberoidOpenAPISpecTemplateMatchesFile(t *testing.T) {
11
+	specFile, err := ioutil.ReadFile("../../api/openapi-spec/blubberoid.yaml")
12
+
13
+	if assert.NoError(t, err) {
14
+		assert.Equal(t, string(specFile), openAPISpecTemplate)
15
+	}
16
+}

+ 11
- 0
scripts/generate-const.sh View File

@@ -0,0 +1,11 @@
1
+#!/bin/bash
2
+#
3
+# Generates a Go const from the contents of a static file, and appends it at
4
+# the line following the go:generate directive that calls this script.
5
+#
6
+set -euo pipefail
7
+
8
+sed -i.sed.bak -e "$((GOLINE+1)),\$d" "$GOFILE"
9
+rm "$GOFILE.sed.bak"
10
+
11
+(echo -n "const $1 = \`"; cat "$2"; echo "\`") >> "$GOFILE"

Loading…
Cancel
Save