Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding a new updateImage method to enable partners to re-run #782

Merged
merged 1 commit into from
Sep 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions certification/pyxis/pyxis.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,51 @@ func (p *pyxisClient) getImage(ctx context.Context, dockerImageDigest string) (*
return &data.Data[0], nil
}

// updateImage updates a given certification image based on how the image is built in the `submit` flow
func (p *pyxisClient) updateImage(ctx context.Context, certImage *CertImage) (*CertImage, error) {
// instantiating a patchCertImage struct, so we only send the minimum fields required to pyxis
patchCertImage := &CertImage{
ID: certImage.ID,
Architecture: certImage.Architecture,
Certified: certImage.Certified,
}

b, err := json.Marshal(patchCertImage)
if err != nil {
return nil, fmt.Errorf("could not marshal certImage: %w", err)
}
req, err := p.newRequestWithAPIToken(ctx, http.MethodPatch, p.getPyxisURL(fmt.Sprintf("images/id/%s", patchCertImage.ID)), bytes.NewReader(b))
if err != nil {
return nil, err
}

resp, err := p.Client.Do(req)
if err != nil {
return nil, fmt.Errorf("cannot update image in pyxis: %w", err)
}

defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("could not read response body: %w", err)
}

if ok := checkStatus(resp.StatusCode); !ok {
return nil, fmt.Errorf(
"status code: %d: body: %s",
resp.StatusCode,
string(body))
}

var updatedCertImage CertImage
if err := json.Unmarshal(body, &updatedCertImage); err != nil {
return nil, fmt.Errorf("could not unmarshal body: %s: %w", string(body), err)
}

return &updatedCertImage, nil
}

// FindImagesByDigest uses an unauthenticated call to find_images() graphql function, and will
// return a slice of CertImages. It accepts a slice of image digests. The query return is then
// packed into the slice of CertImages.
Expand Down
11 changes: 11 additions & 0 deletions certification/pyxis/pyxis_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"

. "github.com/onsi/ginkgo/v2/dsl/core"
Expand Down Expand Up @@ -90,6 +91,14 @@ func (p *pyxisImageHandler) ServeHTTP(response http.ResponseWriter, request *htt
responseString := `{"_id":"blah","certified":false,"deleted":false,"image_id":"123456789abc"}`
log.Tracef("Method: %s", request.Method)
switch {
case request.Method == http.MethodPost && strings.Contains(request.Header["X-Api-Key"][0], "my-update-image"):
response.WriteHeader(http.StatusConflict)
case request.Method == http.MethodGet && strings.Contains(request.Header["X-Api-Key"][0], "my-update-image"):
mustWrite(response, `{"data":[{"_id":"updateImage","certified":false,"deleted":false,"image_id":"123456789abc"}]}`)
case request.Method == http.MethodPatch && request.Header["X-Api-Key"][0] == "my-update-image-success-api-token":
mustWrite(response, `{"_id":"updateImage","certified":true,"deleted":false,"image_id":"123456789abc"}`)
case request.Method == http.MethodPatch && request.Header["X-Api-Key"][0] == "my-update-image-failure-api-token":
response.WriteHeader(http.StatusInternalServerError)
case request.Method == http.MethodPost && request.Header["X-Api-Key"][0] == "my-image-409-api-token":
response.WriteHeader(http.StatusConflict)
case request.Method == http.MethodPost && request.Header["X-Api-Key"][0] == "my-bad-401-image-api-token":
Expand Down Expand Up @@ -128,6 +137,8 @@ func (p *pyxisRPMManifestHandler) ServeHTTP(response http.ResponseWriter, reques
response.WriteHeader(http.StatusUnauthorized)
case request.Header["X-Api-Key"][0] == "my-bad-rpmmanifest-api-token":
response.WriteHeader(http.StatusUnauthorized)
case request.Method == http.MethodPost && request.Header["X-Api-Key"][0] == "my-update-image-success-api-token":
mustWrite(response, `{"_id":"updateImage"}`)
default:
mustWrite(response, `{"_id":"blah"}`)
}
Expand Down
16 changes: 16 additions & 0 deletions certification/pyxis/submit.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ func (p *pyxisClient) SubmitResults(ctx context.Context, certInput *Certificatio
// in the event that it exists. createImage will wipe it otherwise.
originalImageDigest := certImage.DockerImageDigest

// store the certification status for this execution, in case a previous execution failed and we need to patch the image
certified := certInput.CertImage.Certified

// normalizing index.docker.io to docker.io for the certImage
certImage.Repositories[0].Registry = normalizeDockerRegistry(certImage.Repositories[0].Registry)

Expand All @@ -68,6 +71,19 @@ func (p *pyxisClient) SubmitResults(ctx context.Context, certInput *Certificatio
if err != nil {
return nil, fmt.Errorf("could not get image: %v", err)
}

// checking to see if the original value is certified and the previous value is not certified,
// this would indicate that a partner is running preflight again, and during the first run there was a timeout/error
// in a check that interacts with pyxis and we need to correct the certified value for the image
if certified && !certImage.Certified {
// change the certified value to `true`
certImage.Certified = certified

certImage, err = p.updateImage(ctx, certImage)
if err != nil {
return nil, fmt.Errorf("could not update image: %v", err)
}
}
}

// Create the RPM manifest, or get it if it already exists.
Expand Down
40 changes: 40 additions & 0 deletions certification/pyxis/submit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ var _ = Describe("Pyxis Submit", func() {
mux.Handle("/api/v1/projects/certification/id/my-awesome-project-id/test-results", &pyxisTestResultsHandler{})
mux.Handle("/api/v1/projects/certification/id/my-image-project-id/images", &pyxisImageHandler{})
mux.Handle("/api/v1/projects/certification/id/", &pyxisProjectHandler{})
mux.Handle("/api/v1/images/id/updateImage", &pyxisImageHandler{})
mux.Handle("/api/v1/images/id/blah/", &pyxisRPMManifestHandler{})
mux.Handle("/api/v1/images/id/updateImage/", &pyxisImageHandler{})
acornett21 marked this conversation as resolved.
Show resolved Hide resolved
mux.Handle("/api/v1/images", &pyxisImageHandler{})

BeforeEach(func() {
Expand Down Expand Up @@ -170,6 +172,44 @@ var _ = Describe("Pyxis Submit", func() {
})
})

Context("createImage 409 Conflict, getImage 200, and updateImage 200", func() {
BeforeEach(func() {
pyxisClient.APIToken = "my-update-image-success-api-token"
pyxisClient.ProjectID = "my-image-project-id"
certInput.CertImage.Certified = true
})
Context("when a project is submitted", func() {
Context("and an update token is sent to getImage and createImage is in conflict", func() {
It("should call updateImage and certified flag should be updated", func() {
certResults, err := pyxisClient.SubmitResults(ctx, &certInput)
Expect(err).ToNot(HaveOccurred())
Expect(certResults).ToNot(BeNil())
Expect(certResults.CertProject).ToNot(BeNil())
Expect(certResults.CertImage).ToNot(BeNil())
Expect(certResults.CertImage.Certified).To(Equal(true))
Expect(certResults.TestResults).ToNot(BeNil())
})
})
})
})

Context("createImage 409 Conflict, getImage 200, and updateImage 500", func() {
BeforeEach(func() {
pyxisClient.APIToken = "my-update-image-failure-api-token"
pyxisClient.ProjectID = "my-image-project-id"
certInput.CertImage.Certified = true
})
Context("when a project is submitted", func() {
Context("and an update token is sent to getImage and createImage is in conflict", func() {
It("should call updateImage and error", func() {
certResults, err := pyxisClient.SubmitResults(ctx, &certInput)
Expect(err).To(HaveOccurred())
Expect(certResults).To(BeNil())
})
})
})
})

Context("createImage 500 InternalServerError", func() {
BeforeEach(func() {
pyxisClient.APIToken = "my-bad-500-image-api-token"
Expand Down