diff --git a/certification/pyxis/pyxis_suite_test.go b/certification/pyxis/pyxis_suite_test.go index a2cd9f0b..01525060 100644 --- a/certification/pyxis/pyxis_suite_test.go +++ b/certification/pyxis/pyxis_suite_test.go @@ -59,6 +59,15 @@ func (p *pyxisProjectHandler) ServeHTTP(response http.ResponseWriter, request *h switch { case request.Header["X-Api-Key"][0] == "my-bad-project-api-token": response.WriteHeader(401) + case request.Header["X-Api-Key"][0] == "my-update-project-api-token": + if request.Method == http.MethodGet { + mustWrite(response, `{"_id":"deadb33f","certification_status":"Started","name":"My Spiffy Project","project_status":"Foo","type":"Containers","container":{"docker_config_json":"{}","type":"Containers"}}`) + break + } + + response.WriteHeader(500) + case request.Header["X-Api-Key"][0] == "my-index-docker-io-project-api-token": + mustWrite(response, `{"_id":"deadb33f","certification_status":"Started","name":"My Index Docker IO Project","project_status":"Foo","type":"Containers","container":{"docker_config_json":"{}","type":"Containers","registry":"docker.io", "repository":"my/repo"}}`) case request.Method == http.MethodPost: body, err := io.ReadAll(request.Body) if err != nil { diff --git a/certification/pyxis/submit.go b/certification/pyxis/submit.go index b02c973d..13b2fac0 100644 --- a/certification/pyxis/submit.go +++ b/certification/pyxis/submit.go @@ -4,10 +4,13 @@ import ( "context" "fmt" + "github.com/google/go-containerregistry/pkg/name" "github.com/redhat-openshift-ecosystem/openshift-preflight/certification/errors" log "github.com/sirupsen/logrus" ) +var defaultRegistryAlias = "docker.io" + // SubmitResults takes certInput and sends requests to Pyxis to create or update entries // based on certInput. func (p *pyxisClient) SubmitResults(ctx context.Context, certInput *certificationInput) (*CertificationResults, error) { @@ -17,31 +20,42 @@ func (p *pyxisClient) SubmitResults(ctx context.Context, certInput *certificatio certImage := certInput.CertImage // Submission effectively starts the certification process, so switch - // the status to reflect this if needed. + // the status to reflect this if needed. This only needs to be done for net new projects. + // Existing projects that are in "In Progress" can stay "In Progress" until they moved to "Published" which is triggered + // once an image in a project is moved to "Published" status. The status on the project would stay in "Published" status, + // unless the partner decides to un-publish all of their images. At that point backed systems/processes would move + // the project back to "In Process" and there would still be nothing that preflight need to update on the project. if certProject.CertificationStatus == "Started" { certProject.CertificationStatus = "In Progress" - } - // You must have an existing repository. - if len(certImage.Repositories) == 0 { - return nil, errors.ErrInvalidCertImage - } + // You must have an existing repository. + if len(certImage.Repositories) == 0 { + return nil, errors.ErrInvalidCertImage + } - // Set this project's metadata to match the image that we're certifying. - certProject.Container.Registry = certImage.Repositories[0].Registry - certProject.Container.Repository = certImage.Repositories[0].Repository + // Setting registry to the value we get from certImage from crane and then normalizing + // index.docker.io to docker.io so project info shows properly in the Red Hat Catalog + registry := certImage.Repositories[0].Registry + if registry == name.DefaultRegistry { + registry = defaultRegistryAlias + } - // Compare the original - oldCertProject, err := p.GetProject(ctx) - if err != nil { - return nil, fmt.Errorf("%w: %s", err, "could not retrieve project") - } + // Set this project's metadata to match the image that we're certifying. + certProject.Container.Registry = registry + certProject.Container.Repository = certImage.Repositories[0].Repository - if *certProject != *oldCertProject { - certProject, err = p.updateProject(ctx, certProject) + // Compare the original + oldCertProject, err := p.GetProject(ctx) if err != nil { - log.Error(err, "could not update project") - return nil, err + return nil, fmt.Errorf("%w: %s", err, "could not retrieve project") + } + + if *certProject != *oldCertProject { + certProject, err = p.updateProject(ctx, certProject) + if err != nil { + log.Error(err, "could not update project") + return nil, err + } } } diff --git a/certification/pyxis/submit_test.go b/certification/pyxis/submit_test.go index 8063a41d..64fc4546 100644 --- a/certification/pyxis/submit_test.go +++ b/certification/pyxis/submit_test.go @@ -49,7 +49,83 @@ var _ = Describe("Pyxis Submit", func() { }) }) - Context("updateProject 401 Unauthorized", func() { + Context("when a project is submitted with an empty registry", func() { + BeforeEach(func() { + pyxisClient = NewPyxisClient("my.pyxis.host/api", "my-spiffy-api-token", "my-awesome-project-id", &http.Client{Transport: localRoundTripper{handler: mux}}) + }) + Context("and it is not already In Progress", func() { + It("should get invalid cert image error", func() { + certResults, err := pyxisClient.SubmitResults(ctx, &certificationInput{ + CertProject: &CertProject{CertificationStatus: "Started"}, + CertImage: &CertImage{}, + RpmManifest: &RPMManifest{}, + TestResults: &TestResults{}, + Artifacts: []Artifact{}, + }) + Expect(err).To(MatchError(errors.New("certImage has not been properly populated"))) + Expect(certResults).To(BeNil()) + }) + }) + }) + + Context("when an index.docker.io project is submitted", func() { + BeforeEach(func() { + pyxisClient = NewPyxisClient("my.pyxis.host/api", "my-index-docker-io-project-api-token", "my-index-docker-io-project-api-token", &http.Client{Transport: localRoundTripper{handler: mux}}) + }) + Context("and it is not already In Progress", func() { + It("should switch to In Progress and certResults.CertProject.Container.Registry should equal 'docker.io'", func() { + certResults, err := pyxisClient.SubmitResults(ctx, &certificationInput{ + CertProject: &CertProject{CertificationStatus: "Started"}, + CertImage: &CertImage{ + Repositories: []Repository{ + { + Registry: "index.docker.io", + Repository: "my/repo", + }, + }, + }, + RpmManifest: &RPMManifest{}, + TestResults: &TestResults{}, + Artifacts: []Artifact{}, + }) + Expect(err).ToNot(HaveOccurred()) + Expect(certResults).ToNot(BeNil()) + Expect(certResults.CertProject.Container.Registry).Should(Equal(defaultRegistryAlias)) + Expect(certResults.CertImage).ToNot(BeNil()) + Expect(certResults.TestResults).ToNot(BeNil()) + }) + }) + }) + + Context("updateProject 500 Internal Error", func() { + BeforeEach(func() { + pyxisClient = NewPyxisClient("my.pyxis.host/api", "my-update-project-api-token", "my-awesome-project-id", &http.Client{Transport: localRoundTripper{handler: mux}}) + }) + Context("when a project is submitted", func() { + Context("and the client sends an update token", func() { + It("GetProject should succeed and updateProject should 500 Internal Error", func() { + certResults, err := pyxisClient.SubmitResults(ctx, &certificationInput{ + CertProject: &CertProject{CertificationStatus: "Started"}, + CertImage: &CertImage{ + Repositories: []Repository{ + { + Registry: "my.registry", + Repository: "my/repo", + }, + }, + }, + RpmManifest: &RPMManifest{}, + TestResults: &TestResults{}, + Artifacts: []Artifact{}, + }) + Expect(err).To(MatchError(errors.New("error calling remote API"))) + Expect(certResults).To(BeNil()) + }) + }) + }) + }) + + Context("GetProject 401 Unauthorized", func() { BeforeEach(func() { pyxisClient = NewPyxisClient("my.pyxis.host/api", "my-bad-project-api-token", "my-awesome-project-id", &http.Client{Transport: localRoundTripper{handler: mux}}) })