Skip to content

Commit 04fa236

Browse files
authored
fix: support versioned nexus openapi specification files (#354)
Updated the generator binary to support versioned Nexus OpenAPI specification files, as introduced in oxidecomputer/omicron#9430. Closes SSE-129.
1 parent 97a2a75 commit 04fa236

File tree

5 files changed

+536
-42
lines changed

5 files changed

+536
-42
lines changed

VERSION_OMICRON

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
01bb8750
1+
60f225c

internal/generate/main.go

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ package main
66

77
import (
88
"fmt"
9+
"io"
10+
"net/http"
911
"net/url"
1012
"os"
1113
"path/filepath"
@@ -87,7 +89,7 @@ func loadSDKVersionFromFile(file string) (string, error) {
8789

8890
sdkVersion := strings.TrimSpace(string(version))
8991
if sdkVersion == "" {
90-
return "", fmt.Errorf("sdk version cannot be empty: %s", file)
92+
return "", fmt.Errorf("sdk version cannot be empty: %s", f)
9193
}
9294

9395
return sdkVersion, nil
@@ -96,29 +98,70 @@ func loadSDKVersionFromFile(file string) (string, error) {
9698
func loadAPIFromFile(file string) (*openapi3.T, error) {
9799
wd, err := os.Getwd()
98100
if err != nil {
99-
return nil, fmt.Errorf("error getting current working directory: %v", err)
100-
101+
return nil, fmt.Errorf("error getting current working directory: %w", err)
101102
}
103+
102104
p := filepath.Join(filepath.Dir(wd), file)
103105
omicronVersion, err := os.ReadFile(p)
104106
if err != nil {
105-
return nil, fmt.Errorf("error retrieving Omicron version: %v", err)
107+
return nil, fmt.Errorf("error retrieving omicron version from %s: %w", p, err)
106108
}
109+
107110
ov := strings.TrimSpace(string(omicronVersion))
111+
if ov == "" {
112+
return nil, fmt.Errorf("omicron version cannot be empty: %s", p)
113+
}
108114

109-
// TODO: actually host the spec here.
110-
// uri := "https://api.oxide.computer"
111-
uri := fmt.Sprintf("https://raw.githubusercontent.com/oxidecomputer/omicron/%s/openapi/nexus.json", ov)
112-
u, err := url.Parse(uri)
115+
specURL, err := getOpenAPISpecURL(ov)
113116
if err != nil {
114-
return nil, fmt.Errorf("error parsing url %q: %v", uri, err)
117+
return nil, fmt.Errorf("error getting openapi specification url: %w", err)
115118
}
116119

117-
// Load the open API spec from the URI.
118-
doc, err := openapi3.NewLoader().LoadFromURI(u)
120+
doc, err := openapi3.NewLoader().LoadFromURI(specURL)
119121
if err != nil {
120-
return nil, fmt.Errorf("error loading openAPI spec from %q: %v", uri, err)
122+
return nil, fmt.Errorf("error loading openapi specification from %q: %w", specURL, err)
121123
}
122124

123125
return doc, nil
124126
}
127+
128+
// getOpenAPISpecURL returns the URL of the versioned OpenAPI specification for
129+
// the given Omicron version.
130+
//
131+
// The upstream Omicron repository contains versioned OpenAPI specifications
132+
// (e.g., nexus-2025120300.0.0-dfe193.json). The nexus-latest.json file is a
133+
// symbolic link to the current versioned specification file. Since
134+
// raw.githubusercontent.com doesn't follow symbolic links, we first fetch the
135+
// symbolic link target to get the versioned filename, then construct the URL
136+
// to the actual versioned specification.
137+
func getOpenAPISpecURL(omicronVersion string) (*url.URL, error) {
138+
rawURL := fmt.Sprintf("https://raw.githubusercontent.com/oxidecomputer/omicron/%s", omicronVersion)
139+
baseURL, err := url.Parse(rawURL)
140+
if err != nil {
141+
return nil, fmt.Errorf("error parsing base url %q: %w", rawURL, err)
142+
}
143+
144+
latestURL := baseURL.JoinPath("openapi", "nexus", "nexus-latest.json")
145+
resp, err := http.DefaultClient.Get(latestURL.String())
146+
if err != nil {
147+
return nil, fmt.Errorf("error fetching latest openapi file from %q: %w", latestURL, err)
148+
}
149+
defer resp.Body.Close()
150+
151+
body, err := io.ReadAll(resp.Body)
152+
if err != nil {
153+
return nil, fmt.Errorf("error reading response body from %q: %w", latestURL, err)
154+
}
155+
156+
if resp.StatusCode != http.StatusOK {
157+
return nil, fmt.Errorf("unexpected status code %d fetching %q: %s",
158+
resp.StatusCode, latestURL, strings.TrimSpace(string(body)))
159+
}
160+
161+
versioned := strings.TrimSpace(string(body))
162+
if versioned == "" {
163+
return nil, fmt.Errorf("versioned filename is empty in %q", latestURL)
164+
}
165+
166+
return baseURL.JoinPath("openapi", "nexus", versioned), nil
167+
}

internal/generate/main_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ func Test_loadAPI(t *testing.T) {
2525
wantErr: "no such file or directory",
2626
},
2727
{
28-
name: "file does not exist",
28+
name: "empty version file",
2929
args: args{"generate/test_utils/INVALID_VERSION"},
30-
wantErr: "error loading openAPI spec from \"https://raw.githubusercontent.com/oxidecomputer/omicron//openapi/nexus.json\"",
30+
wantErr: "omicron version cannot be empty",
3131
},
3232
{
3333
name: "success",

0 commit comments

Comments
 (0)