Skip to content

Commit

Permalink
compose: Add listing cloud composes to the status command
Browse files Browse the repository at this point in the history
Because of how cloud composes are started they may, or may not, have
information about the blueprint used to create them. Handle this by
leaving those fields blank in the output until PR
osbuild/osbuild-composer#4451 is merged.

Related: RHEL-60123
  • Loading branch information
bcl committed Mar 10, 2025
1 parent 04f796d commit f8aabb0
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 2 deletions.
29 changes: 27 additions & 2 deletions cmd/composer-cli/compose/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,33 @@ func init() {
}

func status(cmd *cobra.Command, args []string) (rcErr error) {
w := tabwriter.NewWriter(os.Stdout, 5, 0, 3, ' ', 0)
fmt.Fprintln(w, "ID\tStatus\tTime\tBlueprint\tVersion\tType\tSize")

// Check cloudapi for composes first
if root.Cloud.Exists() {
composes, _ := root.Cloud.ListComposes()
if len(composes) > 0 {
// The cloudapi status is slightly different than the weldrapi
// convert them into consistent statuses
statusMap := map[string]string{"pending": "RUNNING", "success": "FINISHED", "failure": "FAILED"}

for i := range composes {
// Get as much detail as we can about the compose
// This depends on the type of build and how it was started so some fields may
// be blank. Currently no details are available so they are left blank.
bpName, bpVersion, imageType := composeDetails(composes[i].ID)
status, ok := statusMap[composes[i].Status]
if !ok {
status = "Unknown"
}
fmt.Fprintf(w, "%s\t%-8s\t%s\t%-15s\t%s\t%-16s\t%s\n", composes[i].ID, status, "",
bpName, bpVersion, imageType, "")
}
}
}

// Check weldrapi for composes
composes, errors, err := root.Client.ListComposes()
if err != nil {
return root.ExecutionError(cmd, "List Error: %s", err)
Expand All @@ -40,8 +67,6 @@ func status(cmd *cobra.Command, args []string) (rcErr error) {
rcErr = root.ExecutionErrors(cmd, errors)
}

w := tabwriter.NewWriter(os.Stdout, 5, 0, 3, ' ', 0)
fmt.Fprintln(w, "ID\tStatus\tTime\tBlueprint\tVersion\tType\tSize")
composes = weldr.SortComposeStatusV0(composes)
for i := range composes {
c := composes[i]
Expand Down
96 changes: 96 additions & 0 deletions cmd/composer-cli/compose/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,3 +229,99 @@ func TestCmdComposeStatusJSON(t *testing.T) {
assert.Equal(t, []byte(""), stderr)
assert.Equal(t, "GET", mc.Req.Method)
}

func TestComposeStatusCloud(t *testing.T) {
mcc := root.SetupCloudCmdTest(func(request *http.Request) (*http.Response, error) {
var json string
var sc int

if request.URL.Path == "/api/image-builder-composer/v2/composes/" {
// List of composes and their status
sc = 200
json = `[{
"href": "/api/image-builder-composer/v2/composes/008fc5ad-adad-42ec-b412-7923733483a8",
"id": "008fc5ad-adad-42ec-b412-7923733483a8",
"kind": "ComposeStatus",
"image_status": {
"status": "success",
"upload_status": {
"options": {
"artifact_path": "/var/lib/osbuild-composer/artifacts/008fc5ad-adad-42ec-b412-7923733483a8/disk.qcow2"
},
"status": "success",
"type": "local"
},
"upload_statuses": [
{
"options": {
"artifact_path": "/var/lib/osbuild-composer/artifacts/008fc5ad-adad-42ec-b412-7923733483a8/disk.qcow2"
},
"status": "success",
"type": "local"
}
]
},
"status": "success"
},
{
"href": "/api/image-builder-composer/v2/composes/fd4f2e8a-ba12-4cc1-b485-ba0e464bf7c7",
"id": "fd4f2e8a-ba12-4cc1-b485-ba0e464bf7c7",
"kind": "ComposeStatus",
"image_status": {
"error": {
"details": "osbuild did not return any output",
"id": 10,
"reason": "osbuild build failed"
},
"status": "failure"
},
"status": "failure"
}]`
} else if request.URL.Path == "/api/image-builder-composer/v2/composes/008fc5ad-adad-42ec-b412-7923733483a8/metadata" {
sc = 200
json = `{"href": "/api/image-builder-composer/v2/composes/008fc5ad-adad-42ec-b412-7923733483a8/metadata",
"id": "008fc5ad-adad-42ec-b412-7923733483a8",
"kind": "ComposeMetadata",
"packages": [
{
"arch": "x86_64",
"epoch": "1",
"name": "NetworkManager",
"release": "1.fc40",
"sigmd5": "442ad6fb6f6efd73f4386757883c92e7",
"type": "rpm",
"version": "1.46.2"
}]
}`
} else if request.URL.Path == "/api/image-builder-composer/v2/composes/fd4f2e8a-ba12-4cc1-b485-ba0e464bf7c7/metadata" {
sc = 200
json = `{"href":"/api/image-builder-composer/v2/composes/fd4f2e8a-ba12-4cc1-b485-ba0e464bf7c7/metadata","id":"fd4f2e8a-ba12-4cc1-b485-ba0e464bf7c7","kind":"ComposeMetadata"}`
} else {
sc = 404
json = `{"kind":"ComposeError", "...":"unknown url"}`
}

return &http.Response{
StatusCode: sc,
Body: io.NopCloser(bytes.NewReader([]byte(json))),
}, nil
})

// List all of the composes
cmd, out, err := root.ExecuteTest("compose", "status")
require.NotNil(t, out)
defer out.Close()
require.Nil(t, err)
require.NotNil(t, out.Stdout)
require.NotNil(t, out.Stderr)
require.NotNil(t, cmd)
assert.Equal(t, cmd, statusCmd)
stdout, err := io.ReadAll(out.Stdout)
assert.Nil(t, err)
assert.Contains(t, string(stdout), "fd4f2e8a-ba12-4cc1-b485-ba0e464bf7c7 FAILED")
assert.Contains(t, string(stdout), "008fc5ad-adad-42ec-b412-7923733483a8 FINISHED")
stderr, err := io.ReadAll(out.Stderr)
assert.Nil(t, err)
assert.Equal(t, []byte(""), stderr)
assert.Equal(t, "GET", mcc.Req.Method)
}

0 comments on commit f8aabb0

Please sign in to comment.