Repository: osbuild/osbuild-composer
Pull request #1007 (2020-09-24 07:28:44)

21
Release osbuild-composer version 21.


Patch:
diff --git a/NEWS.md b/NEWS.md
index b8907b071..2336d39f7 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,5 +1,37 @@
 # OSBuild Composer - Operating System Image Composition Services
 
+## CHANGES WITH 21:
+
+  * Composer API is now available as a tech preview in the
+    osbuild-composer-cloud subpackage. It's meant to be a simple API that
+    allows users build an image and push it to a cloud provider. It doesn't
+    support advanced features like storing blueprints as Weldr API does. This
+    is not stable API, and is subject to incompatible change.
+
+  * Koji API is now available in the -koji subpackage. It can be used
+    to perform an image build and push the result directly to a Koji
+    instance.
+    
+  * Worker API is now completely overhauled. Support for distinguishing
+    architectures is added and the whole API is generated from an OpenAPI
+    spec.
+    
+  * Weldr API's /projects/source/new route now explicitly requires the url
+    field. 
+  
+  * The project now requires Go 1.13.
+
+  * Testing of vmware and ostree images is now greatly improved.
+  
+  * All bash scripts are now checked with shellcheck on the CI.
+
+Contributions from: Alexander Todorov, Lars Karlitski, Major Hayden,
+                    Martin Sehnoutka, Ondřej Budai, Peter Robinson,
+                    Sanne Raymaekers, Tom Gundersen, Xiaofeng Wang
+
+— Liberec, 2020-09-24
+
+
 ## CHANGES WITH 20:
 
   * VMDK images are now stream optimized to be compatible with vCenter by
diff --git a/golang-github-osbuild-composer.spec b/golang-github-osbuild-composer.spec
index 9c058a1cf..0112bcb81 100644
--- a/golang-github-osbuild-composer.spec
+++ b/golang-github-osbuild-composer.spec
@@ -1,6 +1,6 @@
 %global goipath         github.com/osbuild/osbuild-composer
 
-Version:        20
+Version:        21
 
 %gometa
 
diff --git a/internal/kojiapi/api/api.go b/internal/kojiapi/api/api.go
index b5f2978c4..4f4e8dccf 100644
--- a/internal/kojiapi/api/api.go
+++ b/internal/kojiapi/api/api.go
@@ -1,3 +1,5 @@
 //go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen -package=api -generate types,server -o api.gen.go openapi.yml
 
 package api
+
+const BasePath = "/api/composer-koji/v1"
diff --git a/internal/kojiapi/api/openapi.yml b/internal/kojiapi/api/openapi.yml
index 3ebc6710d..ab2e1f190 100644
--- a/internal/kojiapi/api/openapi.yml
+++ b/internal/kojiapi/api/openapi.yml
@@ -1,11 +1,13 @@
 openapi: 3.0.1
 info:
-  version: '1'
   title: OSBuild Composer - Koji
+  version: '1'
   description: Service to build and push images to Koji.
   license:
     name: Apache 2.0
     url: 'https://www.apache.org/licenses/LICENSE-2.0.html'
+servers:
+  - url: /api/composer-koji/v1
 paths:
   /status:
     get:
diff --git a/internal/kojiapi/server.go b/internal/kojiapi/server.go
index 1b2337ac2..f816ca52b 100644
--- a/internal/kojiapi/server.go
+++ b/internal/kojiapi/server.go
@@ -44,7 +44,7 @@ func NewServer(logger *log.Logger, workers *worker.Server, rpmMetadata rpmmd.RPM
 	e.Binder = binder{}
 	e.StdLogger = logger
 
-	api.RegisterHandlers(e, &apiHandlers{s})
+	api.RegisterHandlers(e.Group(api.BasePath), &apiHandlers{s})
 
 	s.server = &http.Server{
 		ErrorLog: logger,
diff --git a/internal/worker/api/api.go b/internal/worker/api/api.go
index b5f2978c4..d379f8f2a 100644
--- a/internal/worker/api/api.go
+++ b/internal/worker/api/api.go
@@ -1,3 +1,5 @@
 //go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen -package=api -generate types,server -o api.gen.go openapi.yml
 
 package api
+
+const BasePath = "/api/worker/v1"
diff --git a/internal/worker/api/openapi.yml b/internal/worker/api/openapi.yml
index 146f83f93..c0a9d02ab 100644
--- a/internal/worker/api/openapi.yml
+++ b/internal/worker/api/openapi.yml
@@ -1,8 +1,10 @@
 openapi: 3.0.0
 info:
-  title: worker
-  version: '1.0'
-servers: []
+  title: OSBuild Composer - Worker
+  version: '1'
+  description: This is an API for workers to request and handle jobs.
+servers:
+  - url: /api/worker/v1
 paths:
   /status:
     get:
diff --git a/internal/worker/client.go b/internal/worker/client.go
index 619862bd7..916d61a3b 100644
--- a/internal/worker/client.go
+++ b/internal/worker/client.go
@@ -48,6 +48,11 @@ func NewClient(baseURL string, conf *tls.Config) (*Client, error) {
 		return nil, err
 	}
 
+	server, err = server.Parse(api.BasePath + "/")
+	if err != nil {
+		panic(err)
+	}
+
 	requester := &http.Client{
 		Transport: &http.Transport{
 			TLSClientConfig: conf,
@@ -58,7 +63,12 @@ func NewClient(baseURL string, conf *tls.Config) (*Client, error) {
 }
 
 func NewClientUnix(path string) *Client {
-	server, err := url.Parse("http://localhost")
+	server, err := url.Parse("http://localhost/")
+	if err != nil {
+		panic(err)
+	}
+
+	server, err = server.Parse(api.BasePath + "/")
 	if err != nil {
 		panic(err)
 	}
@@ -75,9 +85,9 @@ func NewClientUnix(path string) *Client {
 }
 
 func (c *Client) RequestJob() (Job, error) {
-	url, err := c.server.Parse("/jobs")
+	url, err := c.server.Parse("jobs")
 	if err != nil {
-		// This only happens when "/jobs" cannot be parsed.
+		// This only happens when "jobs" cannot be parsed.
 		panic(err)
 	}
 
diff --git a/internal/worker/server.go b/internal/worker/server.go
index 4e4a596d9..ec81739a2 100644
--- a/internal/worker/server.go
+++ b/internal/worker/server.go
@@ -65,7 +65,7 @@ func NewServer(logger *log.Logger, jobs jobqueue.JobQueue, artifactsDir string)
 	e.Binder = binder{}
 	e.StdLogger = logger
 
-	api.RegisterHandlers(e, &apiHandlers{s})
+	api.RegisterHandlers(e.Group(api.BasePath), &apiHandlers{s})
 
 	s.server = &http.Server{
 		ErrorLog: logger,
@@ -277,8 +277,8 @@ func (h *apiHandlers) RequestJob(ctx echo.Context) error {
 
 	return ctx.JSON(http.StatusCreated, requestJobResponse{
 		Id:               jobId,
-		Location:         fmt.Sprintf("/jobs/%v", token),
-		ArtifactLocation: fmt.Sprintf("/jobs/%v/artifacts/", token),
+		Location:         fmt.Sprintf("%s/jobs/%v", api.BasePath, token),
+		ArtifactLocation: fmt.Sprintf("%s/jobs/%v/artifacts/", api.BasePath, token),
 		Type:             "osbuild",
 		Args:             serializedArgs,
 	})
diff --git a/osbuild-composer.spec b/osbuild-composer.spec
index 621fd17f5..ce1372f7e 100644
--- a/osbuild-composer.spec
+++ b/osbuild-composer.spec
@@ -4,7 +4,7 @@
 
 %global goipath         github.com/osbuild/osbuild-composer
 
-Version:        20
+Version:        21
 
 %gometa
 


Test patch:
diff --git a/cmd/osbuild-auth-tests/main_test.go b/cmd/osbuild-auth-tests/main_test.go
index f84f29fc1..79ca912b6 100644
--- a/cmd/osbuild-auth-tests/main_test.go
+++ b/cmd/osbuild-auth-tests/main_test.go
@@ -145,7 +145,7 @@ func TestWorkerAPIAuth(t *testing.T) {
 				require.NoError(t, err)
 				defer ckp.remove()
 
-				testRoute(t, "https://localhost:8700/status", ckp, c.success)
+				testRoute(t, "https://localhost:8700/api/worker/v1/status", ckp, c.success)
 			})
 		}
 	})
@@ -161,7 +161,7 @@ func TestWorkerAPIAuth(t *testing.T) {
 		require.NoError(t, err)
 		defer ckp.remove()
 
-		testRoute(t, "https://localhost:8700/status", ckp, false)
+		testRoute(t, "https://localhost:8700/api/worker/v1/status", ckp, false)
 	})
 
 	t.Run("self-signed certificate", func(t *testing.T) {
@@ -170,7 +170,7 @@ func TestWorkerAPIAuth(t *testing.T) {
 		require.NoError(t, err)
 		defer ckp.remove()
 
-		testRoute(t, "https://localhost:8700/status", ckp, false)
+		testRoute(t, "https://localhost:8700/api/worker/v1/status", ckp, false)
 	})
 }
 
@@ -192,7 +192,7 @@ func TestKojiAPIAuth(t *testing.T) {
 				require.NoError(t, err)
 				defer ckp.remove()
 
-				testRoute(t, "https://localhost/status", ckp, c.success)
+				testRoute(t, "https://localhost/api/composer-koji/v1/status", ckp, c.success)
 			})
 		}
 	})
@@ -208,7 +208,7 @@ func TestKojiAPIAuth(t *testing.T) {
 		require.NoError(t, err)
 		defer ckp.remove()
 
-		testRoute(t, "https://localhost/status", ckp, false)
+		testRoute(t, "https://localhost/api/composer-koji/v1/status", ckp, false)
 	})
 
 	t.Run("self-signed certificate", func(t *testing.T) {
@@ -217,7 +217,7 @@ func TestKojiAPIAuth(t *testing.T) {
 		require.NoError(t, err)
 		defer ckp.remove()
 
-		testRoute(t, "https://localhost/status", ckp, false)
+		testRoute(t, "https://localhost/api/composer-koji/v1/status", ckp, false)
 	})
 }
 
diff --git a/internal/worker/server_test.go b/internal/worker/server_test.go
index e91af891c..c89836e53 100644
--- a/internal/worker/server_test.go
+++ b/internal/worker/server_test.go
@@ -18,7 +18,7 @@ import (
 // Ensure that the status request returns OK.
 func TestStatus(t *testing.T) {
 	server := worker.NewServer(nil, testjobqueue.New(), "")
-	test.TestRoute(t, server, false, "GET", "/status", ``, http.StatusOK, `{"status":"OK"}`, "message")
+	test.TestRoute(t, server, false, "GET", "/api/worker/v1/status", ``, http.StatusOK, `{"status":"OK"}`, "message")
 }
 
 func TestErrors(t *testing.T) {
@@ -29,17 +29,17 @@ func TestErrors(t *testing.T) {
 		ExpectedStatus int
 	}{
 		// Bogus path
-		{"GET", "/foo", ``, http.StatusNotFound},
+		{"GET", "/api/worker/v1/foo", ``, http.StatusNotFound},
 		// Create job with invalid body
-		{"POST", "/jobs", ``, http.StatusBadRequest},
+		{"POST", "/api/worker/v1/jobs", ``, http.StatusBadRequest},
 		// Wrong method
-		{"GET", "/jobs", ``, http.StatusMethodNotAllowed},
+		{"GET", "/api/worker/v1/jobs", ``, http.StatusMethodNotAllowed},
 		// Update job with invalid ID
-		{"PATCH", "/jobs/foo", `{"status":"FINISHED"}`, http.StatusBadRequest},
+		{"PATCH", "/api/worker/v1/jobs/foo", `{"status":"FINISHED"}`, http.StatusBadRequest},
 		// Update job that does not exist, with invalid body
-		{"PATCH", "/jobs/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", ``, http.StatusBadRequest},
+		{"PATCH", "/api/worker/v1/jobs/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", ``, http.StatusBadRequest},
 		// Update job that does not exist
-		{"PATCH", "/jobs/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", `{"status":"FINISHED"}`, http.StatusNotFound},
+		{"PATCH", "/api/worker/v1/jobs/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", `{"status":"FINISHED"}`, http.StatusNotFound},
 	}
 
 	for _, c := range cases {
@@ -67,7 +67,7 @@ func TestCreate(t *testing.T) {
 	_, err = server.Enqueue(arch.Name(), manifest, nil)
 	require.NoError(t, err)
 
-	test.TestRoute(t, server, false, "POST", "/jobs", `{"types":["osbuild"],"arch":"x86_64"}`, http.StatusCreated,
+	test.TestRoute(t, server, false, "POST", "/api/worker/v1/jobs", `{"types":["osbuild"],"arch":"x86_64"}`, http.StatusCreated,
 		`{"type":"osbuild","args":{"manifest":{"pipeline":{},"sources":{}}}}`, "id", "location", "artifact_location")
 }
 
@@ -97,6 +97,6 @@ func TestCancel(t *testing.T) {
 	err = server.Cancel(jobId)
 	require.NoError(t, err)
 
-	test.TestRoute(t, server, false, "GET", fmt.Sprintf("/jobs/%s", token), `{}`, http.StatusOK,
+	test.TestRoute(t, server, false, "GET", fmt.Sprintf("/api/worker/v1/jobs/%s", token), `{}`, http.StatusOK,
 		`{"canceled":true}`)
 }
diff --git a/test/image-tests/koji-compose.py b/test/image-tests/koji-compose.py
index 943c24061..f60efe7ac 100755
--- a/test/image-tests/koji-compose.py
+++ b/test/image-tests/koji-compose.py
@@ -42,7 +42,7 @@ def main(distro):
     cr = compose_request(distro, "https://localhost:4343/kojihub")
     print(json.dumps(cr))
 
-    r = requests.post("https://localhost/compose", json=cr,
+    r = requests.post("https://localhost/api/composer-koji/v1/compose", json=cr,
                       cert=("/etc/osbuild-composer/worker-crt.pem", "/etc/osbuild-composer/worker-key.pem"),
                       verify="/etc/osbuild-composer/ca-crt.pem")
     if r.status_code != 201:
@@ -54,7 +54,7 @@ def main(distro):
     compose_id = r.json()["id"]
 
     while True:
-        r = requests.get(f"https://localhost/compose/{compose_id}",
+        r = requests.get(f"https://localhost/api/composer-koji/v1/compose/{compose_id}",
                          cert=("/etc/osbuild-composer/worker-crt.pem", "/etc/osbuild-composer/worker-key.pem"),
                          verify="/etc/osbuild-composer/ca-crt.pem")
         if r.status_code != 200: