diff --git a/.github/workflows/build-os-image.yml b/.github/workflows/build-os-image.yml index f0d20d790..41087a47e 100644 --- a/.github/workflows/build-os-image.yml +++ b/.github/workflows/build-os-image.yml @@ -151,7 +151,7 @@ jobs: { echo "imageVersion=${IMAGE_VERSION}" echo "imageName=ref/${REF}/stream/${STREAM}/${IMAGE_VERSION}" - echo "imageApiBasePath=constellation/v1/ref/${REF}/stream/${STREAM}/image/${IMAGE_VERSION}" + echo "imageApiBasePath=constellation/v1/ref/${REF}/stream/${STREAM}/${IMAGE_VERSION}/image" } >> "$GITHUB_OUTPUT" if [[ "${REF}" = "-" ]] && [[ "${STREAM}" = "stable" ]]; then @@ -728,22 +728,14 @@ jobs: echo -e "\`\`\`" ) >> "$GITHUB_STEP_SUMMARY" - - name: Checkout - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0 - with: - ref: ${{ !github.event.pull_request.head.repo.fork && github.head_ref || '' }} - - - name: Setup Go environment - uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0 - with: - go-version: "1.19.4" - cache: true - - - name: Add version to versionsapi - if: needs.build-settings.outputs.ref != '-' - uses: ./.github/workflows/versionsapi - with: - ref: ${{ needs.build-settings.outputs.ref }} - stream: ${{ inputs.stream }} - version: ${{ needs.build-settings.outputs.imageVersion }} - add_latest: true + add-version-to-versionsapi: + needs: [upload-image-lookup-table, build-settings] + name: "Add version to versionsapi" + if: needs.build-settings.outputs.ref != '-' + uses: ./.github/workflows/versionsapi.yml + with: + command: add + ref: ${{ needs.build-settings.outputs.ref }} + stream: ${{ inputs.stream }} + version: ${{ needs.build-settings.outputs.imageVersion }} + add_latest: true diff --git a/.github/workflows/generate-measurements.yml b/.github/workflows/generate-measurements.yml index 5b182a52e..150073e62 100644 --- a/.github/workflows/generate-measurements.yml +++ b/.github/workflows/generate-measurements.yml @@ -50,7 +50,7 @@ jobs: - name: Check if image definition from build pipeline exists run: | - wget -O /dev/null "https://cdn.confidential.cloud/constellation/v1/ref/${{ steps.extract.outputs.ref }}/stream/${{ steps.extract.outputs.stream }}/image/${{ steps.extract.outputs.version }}/info.json" + wget -O /dev/null "https://cdn.confidential.cloud/constellation/v1/ref/${{ steps.extract.outputs.ref }}/stream/${{ steps.extract.outputs.stream }}/${{ steps.extract.outputs.version }}/image/info.json" shell: bash - name: Setup Go environment @@ -217,7 +217,7 @@ jobs: - name: Download expected measurements from build pipeline for image run: | - path="constellation/v1/ref/${ref}/stream/${stream}/image/${version}/csp/${{ matrix.provider }}/measurements.image.json" + path="constellation/v1/ref/${ref}/stream/${stream}/${version}/image/csp/${{ matrix.provider }}/measurements.image.json" mkdir -p ${{ github.workspace }}/expected-measurements wget -O ${{ github.workspace }}/expected-measurements/measurements.image.json "https://cdn.confidential.cloud/${path}" cat ${{ github.workspace }}/expected-measurements/measurements.image.json @@ -319,7 +319,7 @@ jobs: - name: Upload to S3 run: | - S3_PATH=s3://cdn-constellation-backend/constellation/v1/ref/${ref}/stream/${stream}/image/${version}/csp/${{ matrix.provider }} + S3_PATH=s3://cdn-constellation-backend/constellation/v1/ref/${ref}/stream/${stream}/${version}/image/csp/${{ matrix.provider }} aws s3 cp "${{ github.workspace }}/generated-measurements/measurements-${{ matrix.provider }}.json" "${S3_PATH}/measurements.json" aws s3 cp "${{ github.workspace }}/generated-measurements/measurements-${{ matrix.provider }}.json.sig" "${S3_PATH}/measurements.json.sig" shell: bash diff --git a/cli/internal/cmd/configfetchmeasurements.go b/cli/internal/cmd/configfetchmeasurements.go index fc84d0d9e..9aa45389d 100644 --- a/cli/internal/cmd/configfetchmeasurements.go +++ b/cli/internal/cmd/configfetchmeasurements.go @@ -195,6 +195,6 @@ func measurementURL(provider cloudprovider.Provider, image, file string) (*url.U if err != nil { return nil, fmt.Errorf("parsing image version repository URL: %w", err) } - url.Path = path.Join(constants.CDNAPIPrefix, "ref", ref, "stream", stream, "image", version, "csp", strings.ToLower(provider.String()), file) + url.Path = path.Join(constants.CDNAPIPrefix, "ref", ref, "stream", stream, version, "image", "csp", strings.ToLower(provider.String()), file) return url, nil } diff --git a/cli/internal/cmd/configfetchmeasurements_test.go b/cli/internal/cmd/configfetchmeasurements_test.go index c746e9ba7..4200ddf49 100644 --- a/cli/internal/cmd/configfetchmeasurements_test.go +++ b/cli/internal/cmd/configfetchmeasurements_test.go @@ -110,8 +110,8 @@ func TestUpdateURLs(t *testing.T) { }, }, flags: &fetchMeasurementsFlags{}, - wantMeasurementsURL: constants.CDNRepositoryURL + "/" + constants.CDNAPIPrefix + "/ref/-/stream/stable/image/someImageVersion/csp/gcp/measurements.json", - wantMeasurementsSigURL: constants.CDNRepositoryURL + "/" + constants.CDNAPIPrefix + "/ref/-/stream/stable/image/someImageVersion/csp/gcp/measurements.json.sig", + wantMeasurementsURL: constants.CDNRepositoryURL + "/" + constants.CDNAPIPrefix + "/ref/-/stream/stable/someImageVersion/image/csp/gcp/measurements.json", + wantMeasurementsSigURL: constants.CDNRepositoryURL + "/" + constants.CDNAPIPrefix + "/ref/-/stream/stable/someImageVersion/image/csp/gcp/measurements.json.sig", }, "both set by user": { conf: &config.Config{}, @@ -186,14 +186,14 @@ func TestConfigFetchMeasurements(t *testing.T) { signature := "MEYCIQDRAQNK2NjHJBGrnw3HQAyBsXMCmVCptBdgA6VZ3IlyiAIhAPG42waF1aFZq7dnjP3b2jsMNUtaKYDQQSazW1AX8jgF" client := newTestClient(func(req *http.Request) *http.Response { - if req.URL.Path == "/constellation/v1/ref/-/stream/stable/image/v999.999.999/csp/gcp/measurements.json" { + if req.URL.Path == "/constellation/v1/ref/-/stream/stable/v999.999.999/image/csp/gcp/measurements.json" { return &http.Response{ StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewBufferString(measurements)), Header: make(http.Header), } } - if req.URL.Path == "/constellation/v1/ref/-/stream/stable/image/v999.999.999/csp/gcp/measurements.json.sig" { + if req.URL.Path == "/constellation/v1/ref/-/stream/stable/v999.999.999/image/csp/gcp/measurements.json.sig" { return &http.Response{ StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewBufferString(signature)), diff --git a/internal/versionsapi/client/client.go b/internal/versionsapi/client/client.go index d0f6a74f7..7b64a6535 100644 --- a/internal/versionsapi/client/client.go +++ b/internal/versionsapi/client/client.go @@ -171,6 +171,11 @@ func (c *Client) DeleteVersion(ctx context.Context, ver versionsapi.Version) err retErr = multierr.Append(retErr, fmt.Errorf("updating latest version: %w", err)) } + c.log.Debugf("Deleting artifact path %s for %s", ver.ArtifactPath(), ver.Version) + if err := c.deletePath(ctx, ver.ArtifactPath()); err != nil { + retErr = multierr.Append(retErr, fmt.Errorf("deleting artifact path: %w", err)) + } + return retErr } diff --git a/internal/versionsapi/imageinfo.go b/internal/versionsapi/imageinfo.go index 516488599..185faff76 100644 --- a/internal/versionsapi/imageinfo.go +++ b/internal/versionsapi/imageinfo.go @@ -41,7 +41,8 @@ func (i ImageInfo) JSONPath() string { constants.CDNAPIPrefix, "ref", i.Ref, "stream", i.Stream, - "image", i.Version, + i.Version, + "image", "info.json", ) } diff --git a/internal/versionsapi/imageinfo_test.go b/internal/versionsapi/imageinfo_test.go index 3f6919456..2e6343bbc 100644 --- a/internal/versionsapi/imageinfo_test.go +++ b/internal/versionsapi/imageinfo_test.go @@ -24,7 +24,7 @@ func TestImageInfoJSONPath(t *testing.T) { Stream: "nightly", Version: "v1.0.0", }, - wantPath: constants.CDNAPIPrefix + "/ref/test-ref/stream/nightly/image/v1.0.0/info.json", + wantPath: constants.CDNAPIPrefix + "/ref/test-ref/stream/nightly/v1.0.0/image/info.json", }, "image info release": { info: ImageInfo{ @@ -32,7 +32,7 @@ func TestImageInfoJSONPath(t *testing.T) { Stream: "stable", Version: "v1.0.0", }, - wantPath: constants.CDNAPIPrefix + "/ref/-/stream/stable/image/v1.0.0/info.json", + wantPath: constants.CDNAPIPrefix + "/ref/-/stream/stable/v1.0.0/image/info.json", }, } @@ -55,7 +55,7 @@ func TestImageInfoURL(t *testing.T) { Stream: "nightly", Version: "v1.0.0", }, - wantURL: constants.CDNRepositoryURL + "/" + constants.CDNAPIPrefix + "/ref/test-ref/stream/nightly/image/v1.0.0/info.json", + wantURL: constants.CDNRepositoryURL + "/" + constants.CDNAPIPrefix + "/ref/test-ref/stream/nightly/v1.0.0/image/info.json", }, "image info release": { info: ImageInfo{ @@ -63,7 +63,7 @@ func TestImageInfoURL(t *testing.T) { Stream: "stable", Version: "v1.0.0", }, - wantURL: constants.CDNRepositoryURL + "/" + constants.CDNAPIPrefix + "/ref/-/stream/stable/image/v1.0.0/info.json", + wantURL: constants.CDNRepositoryURL + "/" + constants.CDNAPIPrefix + "/ref/-/stream/stable/v1.0.0/image/info.json", }, } diff --git a/internal/versionsapi/version.go b/internal/versionsapi/version.go index 43313ca0e..37a50dd46 100644 --- a/internal/versionsapi/version.go +++ b/internal/versionsapi/version.go @@ -143,16 +143,14 @@ func (v Version) ListPath(gran Granularity) string { ) } -// ImagePath returns the path to the image specification of this version. +// ArtifactPath returns the path to the artifacts stored for this version. // The path points to a directory and is intended to be used for deletion. -// -// TODO(katexochen): Refactor path and make this a unified path for all version kinds. -func (v Version) ImagePath() string { +func (v Version) ArtifactPath() string { return path.Join( constants.CDNAPIPrefix, "ref", v.Ref, "stream", v.Stream, - "image", v.Version, + v.Version, ) } diff --git a/internal/versionsapi/version_test.go b/internal/versionsapi/version_test.go index c5070a9da..690f16dae 100644 --- a/internal/versionsapi/version_test.go +++ b/internal/versionsapi/version_test.go @@ -373,7 +373,7 @@ func TestVersionListPathURL(t *testing.T) { } } -func TestVersionImagePath(t *testing.T) { +func TestVersionArtifactPath(t *testing.T) { testCases := map[string]struct { ver Version wantPath string @@ -385,7 +385,7 @@ func TestVersionImagePath(t *testing.T) { Version: "v9.9.9", Kind: VersionKindImage, }, - wantPath: constants.CDNAPIPrefix + "/ref/" + ReleaseRef + "/stream/stable/image/v9.9.9", + wantPath: constants.CDNAPIPrefix + "/ref/" + ReleaseRef + "/stream/stable/v9.9.9", }, "release debug stream": { ver: Version{ @@ -394,7 +394,7 @@ func TestVersionImagePath(t *testing.T) { Version: "v9.9.9", Kind: VersionKindImage, }, - wantPath: constants.CDNAPIPrefix + "/ref/" + ReleaseRef + "/stream/debug/image/v9.9.9", + wantPath: constants.CDNAPIPrefix + "/ref/" + ReleaseRef + "/stream/debug/v9.9.9", }, "branch ref": { ver: Version{ @@ -403,7 +403,7 @@ func TestVersionImagePath(t *testing.T) { Version: "v9.9.9", Kind: VersionKindImage, }, - wantPath: constants.CDNAPIPrefix + "/ref/foo/stream/debug/image/v9.9.9", + wantPath: constants.CDNAPIPrefix + "/ref/foo/stream/debug/v9.9.9", }, } @@ -411,7 +411,7 @@ func TestVersionImagePath(t *testing.T) { t.Run(name, func(t *testing.T) { assert := assert.New(t) - path := tc.ver.ImagePath() + path := tc.ver.ArtifactPath() assert.Equal(tc.wantPath, path) }) } diff --git a/rfc/apis.md b/rfc/apis.md index 52df6affb..eeaa58966 100644 --- a/rfc/apis.md +++ b/rfc/apis.md @@ -27,12 +27,12 @@ There may be more API groups in the future (e.g. `cli`) - [`/constellation/v1/ref//stream//versions/latest/.json`](version-api.md#latest) - [`/constellation/v1/ref//stream//versions/major//.json`](version-api.md#major-to-minor-version-list) - [`/constellation/v1/ref//stream//versions/minor//.json`](version-api.md#minor-to-patch-version-list) -- [`/constellation/v1/ref//stream//image//info.json`](image-api.md#image-lookup-table) -- [`/constellation/v1/ref//stream//image//sbom..json`](image-api.md) -- [`/constellation/v1/ref//stream//image//csp//measurements.json`](image-api.md) -- [`/constellation/v1/ref//stream//image//csp//measurements.json.sig`](image-api.md) -- [`/constellation/v1/ref//stream//image//csp//measurements.image.json`](image-api.md) -- [`/constellation/v1/ref//stream//image//csp//image.raw`](image-api.md) +- [`/constellation/v1/ref//stream///image/info.json`](image-api.md#image-lookup-table) +- [`/constellation/v1/ref//stream///image/sbom..json`](image-api.md) +- [`/constellation/v1/ref//stream///image/csp//measurements.json`](image-api.md) +- [`/constellation/v1/ref//stream///image/csp//measurements.json.sig`](image-api.md) +- [`/constellation/v1/ref//stream///image/csp//measurements.image.json`](image-api.md) +- [`/constellation/v1/ref//stream///image/csp//image.raw`](image-api.md) ## API path identifiers `ref`, `stream` and `version` diff --git a/rfc/image-api.md b/rfc/image-api.md index 31ba1bedb..2c8e4f647 100644 --- a/rfc/image-api.md +++ b/rfc/image-api.md @@ -71,7 +71,7 @@ Where applicable, the API uses the following CSP names: The following HTTP endpoints are available: -- `GET /constellation/v1/ref//stream//image//` +- `GET /constellation/v1/ref//stream///image/` - [`info.json` returns the lookup table for the given image version.](#image-lookup-table) - `sbom..json` contains SBOM files for the given image version. The exact formats and file names are TBD. - `GET /constellation/v1/ref//stream///csp//` contains files with measurements and signatures for the given image version and CSP. @@ -87,7 +87,7 @@ The following HTTP endpoints are available: The image lookup table is a JSON file that maps the image name consisting of `ref`, `stream` and `version` to the CSP-specific image references: ``` -/constellation/v1/ref//stream//image//info.json +/constellation/v1/ref//stream///image/info.json ``` ```json @@ -108,7 +108,7 @@ The image lookup table is a JSON file that maps the image name consisting of `re "sev-es": "gcp-image-123" }, "qemu": { - "default": "https://cdn.confidential.cloud/constellation/v1/ref//stream//image//csp/qemu/image.raw" + "default": "https://cdn.confidential.cloud/constellation/v1/ref//stream///image/csp/qemu/image.raw" } } ``` @@ -133,7 +133,7 @@ The `image` field is independent of the CSP and is a used to discover the CSP-sp The CLI can find a CSP- and region specific image reference by looking up the image name in the following order: - if a local file `.json` exists, use the lookup table in that file -- otherwise, load the image lookup table from a well known URL (e.g. `https://cdn.confidential.cloud/constellation/v1/ref//stream//image//info.json`) and use the lookup table in that file +- otherwise, load the image lookup table from a well known URL (e.g. `https://cdn.confidential.cloud/constellation/v1/ref//stream///image/info.json`) and use the lookup table in that file - choose the CSP-specific image reference for the current region and security type: - On AWS, use the AMI ID for the current region (e.g. `.aws.us-east-1`) - On Azure, use the image ID for the security type (CVM or Trusted Launch) (e.g. `.azure.cvm`)