Repository: redhat-openshift-ecosystem/openshift-preflight
Pull request #448 (2022-03-01 17:02:59)

adding in a call to pyxis getImage so preflight can run multiple times
- Fixes: #442 
- adding in call to `projects/certification/id/%s/images?filter=docker_image_digest==%s` to see if the image already exists in pyxis
- Relates: #369 
- added call to `images/id/%s/rpm-manifest` to get an existing rpm manifest (**note: this route isn't fully functioning in the external pyxis api yet**)
- adding in tests to cover all new and existing methods in pyxis package

Signed-off-by: Adam D. Cornett <adc@redhat.com>

openshift-ci[bot]: @acornett21: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
ci/prow/4.8-e2e | 41eb28da85b67d0f5c8823b4aafdfbbf714044df | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-openshift-ecosystem_openshift-preflight/448/pull-ci-redhat-openshift-ecosystem-openshift-preflight-main-4.8-e2e/1499422167849242624) | true | `/test 4.8-e2e`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=redhat-openshift-ecosystem&repo=openshift-preflight&pr=448). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:acornett21).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->

Patch:
diff --git a/certification/errors/errors.go b/certification/errors/errors.go
index 23cbd18..cb70465 100644
--- a/certification/errors/errors.go
+++ b/certification/errors/errors.go
@@ -30,4 +30,5 @@ var (
 	ErrUnsupportedGoType               = errors.New("go type unsupported")
 	ErrSaveFileFailed                  = errors.New("failed to save file to artifacts directory")
 	ErrNon200StatusCode                = errors.New("error calling remote API")
+	Err409StatusCode                   = errors.New("remote API returned conflict")
 )
diff --git a/certification/pyxis/pyxis.go b/certification/pyxis/pyxis.go
index 41db525..b8a756e 100644
--- a/certification/pyxis/pyxis.go
+++ b/certification/pyxis/pyxis.go
@@ -51,6 +51,8 @@ func (p *pyxisEngine) createImage(ctx context.Context, certImage *CertImage) (*C
 		return nil, err
 	}
 
+	log.Debugf("URL is: %s", req.URL)
+
 	resp, err := p.Client.Do(req)
 	if err != nil {
 		log.Error(err)
@@ -65,6 +67,10 @@ func (p *pyxisEngine) createImage(ctx context.Context, certImage *CertImage) (*C
 		return nil, err
 	}
 
+	if resp.StatusCode == 409 {
+		return nil, errors.Err409StatusCode
+	}
+
 	if !checkStatus(resp.StatusCode) {
 		log.Errorf("%s: %s", "received non 200 status code in createImage", string(body))
 		return nil, errors.ErrNon200StatusCode
@@ -79,6 +85,48 @@ func (p *pyxisEngine) createImage(ctx context.Context, certImage *CertImage) (*C
 	return &newCertImage, nil
 }
 
+func (p *pyxisEngine) getImage(ctx context.Context, dockerImageDigest string) (*CertImage, error) {
+	req, err := p.newRequestWithApiToken(ctx, http.MethodGet,
+		getPyxisUrl(fmt.Sprintf("projects/certification/id/%s/images?filter=docker_image_digest==%s", p.ProjectId, dockerImageDigest)), nil)
+	if err != nil {
+		log.Error(err)
+		return nil, err
+	}
+
+	log.Debugf("URL is: %s", req.URL)
+
+	resp, err := p.Client.Do(req)
+	if err != nil {
+		log.Error(err)
+		return nil, err
+	}
+
+	defer resp.Body.Close()
+
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		log.Error(err)
+		return nil, err
+	}
+
+	if !checkStatus(resp.StatusCode) {
+		log.Errorf("%s: %s", "received non 200 status code in getImage", string(body))
+		return nil, errors.ErrNon200StatusCode
+	}
+
+	// using an inline struct since this api's response is in a different format
+	data := struct {
+		Data []CertImage `json:"data,omitempty"`
+	}{}
+
+	if err := json.Unmarshal(body, &data); err != nil {
+		log.Error(err)
+		return nil, err
+	}
+
+	return &data.Data[0], nil
+}
+
 func (p *pyxisEngine) createRPMManifest(ctx context.Context, rpmManifest *RPMManifest) (*RPMManifest, error) {
 	b, err := json.Marshal(rpmManifest)
 	if err != nil {
@@ -91,6 +139,8 @@ func (p *pyxisEngine) createRPMManifest(ctx context.Context, rpmManifest *RPMMan
 		return nil, err
 	}
 
+	log.Debugf("URL is: %s", req.URL)
+
 	resp, err := p.Client.Do(req)
 	if err != nil {
 		log.Error(err)
@@ -105,6 +155,10 @@ func (p *pyxisEngine) createRPMManifest(ctx context.Context, rpmManifest *RPMMan
 		return nil, err
 	}
 
+	if resp.StatusCode == 409 {
+		return nil, errors.Err409StatusCode
+	}
+
 	if !checkStatus(resp.StatusCode) {
 		log.Errorf("%s: %s", "received non 200 status code in createRPMManifest", string(body))
 		return nil, errors.ErrNon200StatusCode
@@ -119,6 +173,43 @@ func (p *pyxisEngine) createRPMManifest(ctx context.Context, rpmManifest *RPMMan
 	return &newRPMManifest, nil
 }
 
+func (p *pyxisEngine) getRPMManifest(ctx context.Context, imageID string) (*RPMManifest, error) {
+	req, err := p.newRequestWithApiToken(ctx, http.MethodGet, getPyxisUrl(fmt.Sprintf("images/id/%s/rpm-manifest", imageID)), nil)
+	if err != nil {
+		log.Error(err)
+		return nil, err
+	}
+
+	log.Debugf("URL is: %s", req.URL)
+
+	resp, err := p.Client.Do(req)
+	if err != nil {
+		log.Error(err)
+		return nil, err
+	}
+
+	defer resp.Body.Close()
+
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		log.Error(err)
+		return nil, err
+	}
+
+	if !checkStatus(resp.StatusCode) {
+		log.Errorf("%s: %s", "received non 200 status code in getRPMManifest", string(body))
+		return nil, errors.ErrNon200StatusCode
+	}
+
+	var newRPMManifest RPMManifest
+	if err := json.Unmarshal(body, &newRPMManifest); err != nil {
+		log.Error(err)
+		return nil, err
+	}
+
+	return &newRPMManifest, nil
+}
+
 func (p *pyxisEngine) GetProject(ctx context.Context) (*CertProject, error) {
 	req, err := p.newRequestWithApiToken(ctx, http.MethodGet, getPyxisUrl(fmt.Sprintf("projects/certification/id/%s", p.ProjectId)), nil)
 	if err != nil {
@@ -167,6 +258,8 @@ func (p *pyxisEngine) updateProject(ctx context.Context, certProject *CertProjec
 		return nil, err
 	}
 
+	log.Debugf("URL is: %s", req.URL)
+
 	resp, err := p.Client.Do(req)
 	if err != nil {
 		log.Error(err)
diff --git a/certification/pyxis/submit.go b/certification/pyxis/submit.go
index 318f626..b78352c 100644
--- a/certification/pyxis/submit.go
+++ b/certification/pyxis/submit.go
@@ -3,19 +3,20 @@ package pyxis
 import (
 	"context"
 
+	"github.com/redhat-openshift-ecosystem/openshift-preflight/certification/errors"
 	log "github.com/sirupsen/logrus"
 )
 
 func (p *pyxisEngine) SubmitResults(certProject *CertProject, certImage *CertImage, rpmManifest *RPMManifest, testResults *TestResults) (*CertProject, *CertImage, *TestResults, error) {
 	var err error
 	ctx := context.Background()
-	oldProject := certProject
+	oldProject := *certProject
 
 	if certProject.CertificationStatus == "Started" {
 		certProject.CertificationStatus = "In Progress"
 	}
 
-	if certProject != oldProject {
+	if *certProject != oldProject {
 		certProject, err = p.updateProject(ctx, certProject)
 		if err != nil {
 			log.Error(err, "could not update project")
@@ -23,18 +24,34 @@ func (p *pyxisEngine) SubmitResults(certProject *CertProject, certImage *CertIma
 		}
 	}
 
+	dockerImageDigest := certImage.DockerImageDigest
+
 	certImage, err = p.createImage(ctx, certImage)
-	if err != nil {
+	if err != nil && err != errors.Err409StatusCode {
 		log.Error(err, "could not create image")
 		return nil, nil, nil, err
 	}
+	if err != nil && err == errors.Err409StatusCode {
+		certImage, err = p.getImage(ctx, dockerImageDigest)
+		if err != nil {
+			log.Error(err, "could not get image")
+			return nil, nil, nil, err
+		}
+	}
 
 	rpmManifest.ImageID = certImage.ID
 	_, err = p.createRPMManifest(ctx, rpmManifest)
-	if err != nil {
+	if err != nil && err != errors.Err409StatusCode {
 		log.Error(err, "could not create rpm manifest")
 		return nil, nil, nil, err
 	}
+	if err != nil && err == errors.Err409StatusCode {
+		_, err = p.getRPMManifest(ctx, rpmManifest.ImageID)
+		if err != nil {
+			log.Error(err, "could not get rpm manifest")
+			return nil, nil, nil, err
+		}
+	}
 
 	testResults.ImageID = certImage.ID
 	testResults, err = p.createTestResults(ctx, testResults)


Test patch:
diff --git a/certification/pyxis/pyxis_suite_test.go b/certification/pyxis/pyxis_suite_test.go
index cd25e78..33caa32 100644
--- a/certification/pyxis/pyxis_suite_test.go
+++ b/certification/pyxis/pyxis_suite_test.go
@@ -28,17 +28,222 @@ func (fhc fakeHttpClient) Do(req *http.Request) (*http.Response, error) {
 	var results string
 
 	switch {
+	case strings.Contains(req.URL.Path, "test-results"):
+		results = `{"image": "quay.io/awesome/image:latest", "passed": false}`
 	case strings.Contains(req.URL.Path, "certification"):
 		results = `{"certification_status":"Started","name":"My Spiffy Project","project_status":"Foo","type":"Containers","container":{"docker_config_json":"{}","type":"Containers"}}`
 
 		if req.Method == http.MethodPatch {
 			results = `{"certification_status":"In Progress","name":"My Spiffy Project","project_status":"Foo","type":"Containers","container":{"docker_config_json":"{}","type":"Containers"}}`
 		}
+	case strings.Contains(req.URL.Path, "rpm-manifest"):
+		results = `{"object_type": "containerImageRPMManifest"}`
 	case strings.Contains(req.URL.Path, "images"):
-		results = `{"certified":false,"deleted":false}`
-	case strings.Contains(req.URL.Path, "test-results"):
-		results = `{"image": "quay.io/awesome/image:latest", "passed": false,}`
+		results = `{"certified":false,"deleted":false,"image_id":"123456789abc"}`
 	}
 
 	return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewReader([]byte(results)))}, nil
 }
+
+type fakeHttpCertProjectUnauthorizedClient struct{}
+
+func (fhc fakeHttpCertProjectUnauthorizedClient) Do(req *http.Request) (*http.Response, error) {
+	var results string
+
+	switch {
+	case strings.Contains(req.URL.Path, "certification"):
+		results = `{}`
+	}
+
+	return &http.Response{StatusCode: http.StatusUnauthorized, Body: io.NopCloser(bytes.NewReader([]byte(results)))}, nil
+}
+
+type fakeHttpCreateImageConflictClient struct{}
+
+func (fhc fakeHttpCreateImageConflictClient) Do(req *http.Request) (*http.Response, error) {
+	var results string
+	statusCode := http.StatusOK
+
+	switch {
+	case strings.Contains(req.URL.Path, "test-results"):
+		results = `{"image": "quay.io/awesome/image:latest", "passed": false}`
+	case strings.Contains(req.URL.RawQuery, "filter=docker_image_digest=="):
+		results = `{"data":[{"certified":false,"deleted":false,"image_id":"123456789abc"}]}`
+	case strings.Contains(req.URL.Path, "certification"):
+		results = `{"certification_status":"Started","name":"My Spiffy Project","project_status":"Foo","type":"Containers","container":{"docker_config_json":"{}","type":"Containers"}}`
+
+		if req.Method == http.MethodPatch {
+			results = `{"certification_status":"In Progress","name":"My Spiffy Project","project_status":"Foo","type":"Containers","container":{"docker_config_json":"{}","type":"Containers"}}`
+		}
+	case strings.Contains(req.URL.Path, "rpm-manifest"):
+		results = `{"object_type": "containerImageRPMManifest"}`
+	case strings.Contains(req.URL.Path, "images"):
+		results = ``
+		statusCode = http.StatusConflict
+	}
+
+	return &http.Response{StatusCode: statusCode, Body: io.NopCloser(bytes.NewReader([]byte(results)))}, nil
+}
+
+type fakeHttpCreateImageUnauthorizedClient struct{}
+
+func (fhc fakeHttpCreateImageUnauthorizedClient) Do(req *http.Request) (*http.Response, error) {
+	var results string
+	statusCode := http.StatusOK
+
+	switch {
+	case strings.Contains(req.URL.Path, "certification"):
+		results = `{"certification_status":"Started","name":"My Spiffy Project","project_status":"Foo","type":"Containers","container":{"docker_config_json":"{}","type":"Containers"}}`
+
+		if req.Method == http.MethodPatch {
+			results = `{"certification_status":"In Progress","name":"My Spiffy Project","project_status":"Foo","type":"Containers","container":{"docker_config_json":"{}","type":"Containers"}}`
+		}
+	case strings.Contains(req.URL.Path, "images"):
+		results = `{}`
+		statusCode = http.StatusUnauthorized
+	}
+
+	return &http.Response{StatusCode: statusCode, Body: io.NopCloser(bytes.NewReader([]byte(results)))}, nil
+}
+
+type fakeHttpCreateImageConflictAndUnauthorizedClient struct{}
+
+func (fhc fakeHttpCreateImageConflictAndUnauthorizedClient) Do(req *http.Request) (*http.Response, error) {
+	var results string
+	statusCode := http.StatusOK
+
+	switch {
+	case strings.Contains(req.URL.Path, "test-results"):
+		results = `{"image": "quay.io/awesome/image:latest", "passed": false}`
+	case strings.Contains(req.URL.RawQuery, "filter=docker_image_digest=="):
+		results = ``
+		statusCode = http.StatusUnauthorized
+	case strings.Contains(req.URL.Path, "certification"):
+		results = `{"certification_status":"Started","name":"My Spiffy Project","project_status":"Foo","type":"Containers","container":{"docker_config_json":"{}","type":"Containers"}}`
+
+		if req.Method == http.MethodPatch {
+			results = `{"certification_status":"In Progress","name":"My Spiffy Project","project_status":"Foo","type":"Containers","container":{"docker_config_json":"{}","type":"Containers"}}`
+		}
+	case strings.Contains(req.URL.Path, "rpm-manifest"):
+		results = `{"object_type": "containerImageRPMManifest"}`
+	case strings.Contains(req.URL.Path, "images"):
+		results = ``
+		statusCode = http.StatusConflict
+	}
+
+	return &http.Response{StatusCode: statusCode, Body: io.NopCloser(bytes.NewReader([]byte(results)))}, nil
+}
+
+type fakeHttpCreateRPMManifestConflictClient struct{}
+
+func (fhc fakeHttpCreateRPMManifestConflictClient) Do(req *http.Request) (*http.Response, error) {
+	var results string
+	statusCode := http.StatusOK
+
+	switch {
+	case strings.Contains(req.URL.Path, "test-results"):
+		results = `{"image": "quay.io/awesome/image:latest", "passed": false}`
+	case strings.Contains(req.URL.Path, "certification"):
+		results = `{"certification_status":"Started","name":"My Spiffy Project","project_status":"Foo","type":"Containers","container":{"docker_config_json":"{}","type":"Containers"}}`
+
+		if req.Method == http.MethodPatch {
+			results = `{"certification_status":"In Progress","name":"My Spiffy Project","project_status":"Foo","type":"Containers","container":{"docker_config_json":"{}","type":"Containers"}}`
+		}
+	case strings.Contains(req.URL.Path, "rpm-manifest"):
+		if req.Method == http.MethodPost {
+			results = `{}`
+			statusCode = http.StatusConflict
+		}
+
+		if req.Method == http.MethodGet {
+			results = `{"object_type": "containerImageRPMManifest"}`
+		}
+	case strings.Contains(req.URL.Path, "images"):
+		results = `{"certified":false,"deleted":false,"image_id":"123456789abc"}`
+
+	}
+
+	return &http.Response{StatusCode: statusCode, Body: io.NopCloser(bytes.NewReader([]byte(results)))}, nil
+}
+
+type fakeHttpCreateRPMManifestConflictAndUnauthorizedClient struct{}
+
+func (fhc fakeHttpCreateRPMManifestConflictAndUnauthorizedClient) Do(req *http.Request) (*http.Response, error) {
+	var results string
+	statusCode := http.StatusOK
+
+	switch {
+	case strings.Contains(req.URL.Path, "test-results"):
+		results = `{"image": "quay.io/awesome/image:latest", "passed": false}`
+	case strings.Contains(req.URL.Path, "certification"):
+		results = `{"certification_status":"Started","name":"My Spiffy Project","project_status":"Foo","type":"Containers","container":{"docker_config_json":"{}","type":"Containers"}}`
+
+		if req.Method == http.MethodPatch {
+			results = `{"certification_status":"In Progress","name":"My Spiffy Project","project_status":"Foo","type":"Containers","container":{"docker_config_json":"{}","type":"Containers"}}`
+		}
+	case strings.Contains(req.URL.Path, "rpm-manifest"):
+		if req.Method == http.MethodPost {
+			results = `{}`
+			statusCode = http.StatusConflict
+		}
+
+		if req.Method == http.MethodGet {
+			results = `{}`
+			statusCode = http.StatusUnauthorized
+		}
+	case strings.Contains(req.URL.Path, "images"):
+		results = `{"certified":false,"deleted":false,"image_id":"123456789abc"}`
+	}
+
+	return &http.Response{StatusCode: statusCode, Body: io.NopCloser(bytes.NewReader([]byte(results)))}, nil
+}
+
+type fakeHttpCreateRPMManifestUnauthorizedClient struct{}
+
+func (fhc fakeHttpCreateRPMManifestUnauthorizedClient) Do(req *http.Request) (*http.Response, error) {
+	var results string
+	statusCode := http.StatusOK
+
+	switch {
+	case strings.Contains(req.URL.Path, "test-results"):
+		results = `{"image": "quay.io/awesome/image:latest", "passed": false}`
+	case strings.Contains(req.URL.Path, "certification"):
+		results = `{"certification_status":"Started","name":"My Spiffy Project","project_status":"Foo","type":"Containers","container":{"docker_config_json":"{}","type":"Containers"}}`
+
+		if req.Method == http.MethodPatch {
+			results = `{"certification_status":"In Progress","name":"My Spiffy Project","project_status":"Foo","type":"Containers","container":{"docker_config_json":"{}","type":"Containers"}}`
+		}
+	case strings.Contains(req.URL.Path, "rpm-manifest"):
+		results = `{}`
+		statusCode = http.StatusUnauthorized
+	case strings.Contains(req.URL.Path, "images"):
+		results = `{"certified":false,"deleted":false,"image_id":"123456789abc"}`
+	}
+
+	return &http.Response{StatusCode: statusCode, Body: io.NopCloser(bytes.NewReader([]byte(results)))}, nil
+}
+
+type fakeHttpCreateTestResultsUnauthorizedClient struct{}
+
+func (fhc fakeHttpCreateTestResultsUnauthorizedClient) Do(req *http.Request) (*http.Response, error) {
+	var results string
+	statusCode := http.StatusOK
+
+	switch {
+	case strings.Contains(req.URL.Path, "test-results"):
+		results = `{}`
+		statusCode = http.StatusUnauthorized
+	case strings.Contains(req.URL.Path, "certification"):
+		results = `{"certification_status":"Started","name":"My Spiffy Project","project_status":"Foo","type":"Containers","container":{"docker_config_json":"{}","type":"Containers"}}`
+
+		if req.Method == http.MethodPatch {
+			results = `{"certification_status":"In Progress","name":"My Spiffy Project","project_status":"Foo","type":"Containers","container":{"docker_config_json":"{}","type":"Containers"}}`
+		}
+	case strings.Contains(req.URL.Path, "rpm-manifest"):
+		results = `{"object_type": "containerImageRPMManifest"}`
+	case strings.Contains(req.URL.Path, "images"):
+		results = `{"certified":false,"deleted":false,"image_id":"123456789abc"}`
+	}
+
+	return &http.Response{StatusCode: statusCode, Body: io.NopCloser(bytes.NewReader([]byte(results)))}, nil
+}
diff --git a/certification/pyxis/submit_test.go b/certification/pyxis/submit_test.go
index e03396e..dc3496b 100644
--- a/certification/pyxis/submit_test.go
+++ b/certification/pyxis/submit_test.go
@@ -1,6 +1,9 @@
 package pyxis
 
 import (
+	"context"
+	"errors"
+
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
 )
@@ -11,6 +14,44 @@ var _ = Describe("Pyxis Submit", func() {
 	BeforeEach(func() {
 		pyxisEngine = NewPyxisEngine("my-spiffy-api-token", "my-awseome-project-id", fakeHttpClient{})
 	})
+	Context("when a project is submitted", func() {
+		Context("and it is not already In Progress", func() {
+			It("should switch to In Progress", func() {
+				certProject, certImage, testResults, err := pyxisEngine.SubmitResults(&CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{})
+				Expect(err).ToNot(HaveOccurred())
+				Expect(certProject).ToNot(BeNil())
+				Expect(certImage).ToNot(BeNil())
+				Expect(testResults).ToNot(BeNil())
+			})
+		})
+	})
+})
+
+var _ = Describe("Pyxis Submit updateProject 401 Unauthorized", func() {
+	var pyxisEngine *pyxisEngine
+
+	BeforeEach(func() {
+		pyxisEngine = NewPyxisEngine("my-spiffy-api-token", "my-awseome-project-id", fakeHttpCertProjectUnauthorizedClient{})
+	})
+	Context("when a project is submitted", func() {
+		Context("and it is not already In Progress", func() {
+			It("should switch to In Progress", func() {
+				certProject, certImage, testResults, err := pyxisEngine.SubmitResults(&CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{})
+				Expect(err).To(MatchError(errors.New("error calling remote API")))
+				Expect(certProject).To(BeNil())
+				Expect(certImage).To(BeNil())
+				Expect(testResults).To(BeNil())
+			})
+		})
+	})
+})
+
+var _ = Describe("Pyxis Submit with createImage 409 Conflict", func() {
+	var pyxisEngine *pyxisEngine
+
+	BeforeEach(func() {
+		pyxisEngine = NewPyxisEngine("my-spiffy-api-token", "my-awseome-project-id", fakeHttpCreateImageConflictClient{})
+	})
 	Context("when a project is submitted", func() {
 		Context("and it is not already In Progress", func() {
 			It("should switch to In Progress", func() {
@@ -23,3 +64,151 @@ var _ = Describe("Pyxis Submit", func() {
 		})
 	})
 })
+
+var _ = Describe("Pyxis Submit with createImage 401 Unauthorized", func() {
+	var pyxisEngine *pyxisEngine
+
+	BeforeEach(func() {
+		pyxisEngine = NewPyxisEngine("my-spiffy-api-token", "my-awseome-project-id", fakeHttpCreateImageUnauthorizedClient{})
+	})
+	Context("when a project is submitted", func() {
+		Context("and it is not already In Progress", func() {
+			It("should switch to In Progress", func() {
+				certProject, certImage, testResults, err := pyxisEngine.SubmitResults(&CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{})
+				Expect(err).To(MatchError(errors.New("error calling remote API")))
+				Expect(certProject).To(BeNil())
+				Expect(certImage).To(BeNil())
+				Expect(testResults).To(BeNil())
+			})
+		})
+	})
+})
+
+var _ = Describe("Pyxis Submit with createImage 409 Conflict and getImage 401 Unauthorized ", func() {
+	var pyxisEngine *pyxisEngine
+
+	BeforeEach(func() {
+		pyxisEngine = NewPyxisEngine("my-spiffy-api-token", "my-awseome-project-id", fakeHttpCreateImageConflictAndUnauthorizedClient{})
+	})
+	Context("when a project is submitted", func() {
+		Context("and it is not already In Progress", func() {
+			It("should switch to In Progress", func() {
+				certProject, certImage, testResults, err := pyxisEngine.SubmitResults(&CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{})
+				Expect(err).To(MatchError(errors.New("error calling remote API")))
+				Expect(certProject).To(BeNil())
+				Expect(certImage).To(BeNil())
+				Expect(testResults).To(BeNil())
+			})
+		})
+	})
+})
+
+var _ = Describe("Pyxis Submit with createRPMManifest 409 Conflict", func() {
+	var pyxisEngine *pyxisEngine
+
+	BeforeEach(func() {
+		pyxisEngine = NewPyxisEngine("my-spiffy-api-token", "my-awseome-project-id", fakeHttpCreateRPMManifestConflictClient{})
+	})
+	Context("when a project is submitted", func() {
+		Context("and it is not already In Progress", func() {
+			It("should switch to In Progress", func() {
+				certProject, certImage, testResults, err := pyxisEngine.SubmitResults(&CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{})
+				Expect(err).ToNot(HaveOccurred())
+				Expect(certProject).ToNot(BeNil())
+				Expect(certImage).ToNot(BeNil())
+				Expect(testResults).ToNot(BeNil())
+			})
+		})
+	})
+})
+
+var _ = Describe("Pyxis Submit with createRPMManifest 401 Unauthorized", func() {
+	var pyxisEngine *pyxisEngine
+
+	BeforeEach(func() {
+		pyxisEngine = NewPyxisEngine("my-spiffy-api-token", "my-awseome-project-id", fakeHttpCreateRPMManifestUnauthorizedClient{})
+	})
+	Context("when a project is submitted", func() {
+		Context("and it is not already In Progress", func() {
+			It("should switch to In Progress", func() {
+				certProject, certImage, testResults, err := pyxisEngine.SubmitResults(&CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{})
+				Expect(err).To(MatchError(errors.New("error calling remote API")))
+				Expect(certProject).To(BeNil())
+				Expect(certImage).To(BeNil())
+				Expect(testResults).To(BeNil())
+			})
+		})
+	})
+})
+
+var _ = Describe("Pyxis Submit with createRPMManifest 409 Conflict and getRPMManifest 401 Unauthorized", func() {
+	var pyxisEngine *pyxisEngine
+
+	BeforeEach(func() {
+		pyxisEngine = NewPyxisEngine("my-spiffy-api-token", "my-awseome-project-id", fakeHttpCreateRPMManifestConflictAndUnauthorizedClient{})
+	})
+	Context("when a project is submitted", func() {
+		Context("and it is not already In Progress", func() {
+			It("should switch to In Progress", func() {
+				certProject, certImage, testResults, err := pyxisEngine.SubmitResults(&CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{})
+				Expect(err).To(MatchError(errors.New("error calling remote API")))
+				Expect(certProject).To(BeNil())
+				Expect(certImage).To(BeNil())
+				Expect(testResults).To(BeNil())
+			})
+		})
+	})
+})
+
+var _ = Describe("Pyxis Submit with createTestResults 401 Unauthorized", func() {
+	var pyxisEngine *pyxisEngine
+
+	BeforeEach(func() {
+		pyxisEngine = NewPyxisEngine("my-spiffy-api-token", "my-awseome-project-id", fakeHttpCreateTestResultsUnauthorizedClient{})
+	})
+	Context("when a project is submitted", func() {
+		Context("and it is not already In Progress", func() {
+			It("should switch to In Progress", func() {
+				certProject, certImage, testResults, err := pyxisEngine.SubmitResults(&CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{})
+				Expect(err).To(MatchError(errors.New("error calling remote API")))
+				Expect(certProject).To(BeNil())
+				Expect(certImage).To(BeNil())
+				Expect(testResults).To(BeNil())
+			})
+		})
+	})
+})
+
+var _ = Describe("Pyxis GetProejct", func() {
+	var pyxisEngine *pyxisEngine
+
+	BeforeEach(func() {
+		pyxisEngine = NewPyxisEngine("my-spiffy-api-token", "my-awseome-project-id", fakeHttpClient{})
+	})
+	Context("when a project is submitted", func() {
+		Context("and it is not already In Progress", func() {
+			It("should switch to In Progress", func() {
+				certProject, err := pyxisEngine.GetProject(context.Background())
+				Expect(err).ToNot(HaveOccurred())
+				Expect(certProject).ToNot(BeNil())
+			})
+		})
+	})
+})
+
+var _ = Describe("Pyxis GetProject 401 Unauthorized", func() {
+	var pyxisEngine *pyxisEngine
+
+	BeforeEach(func() {
+		pyxisEngine = NewPyxisEngine("my-spiffy-api-token", "my-awseome-project-id", fakeHttpCertProjectUnauthorizedClient{})
+	})
+	Context("when a project is submitted", func() {
+		Context("and it is not already In Progress", func() {
+			It("should switch to In Progress", func() {
+				certProject, err := pyxisEngine.GetProject(context.Background())
+				Expect(err).To(MatchError(errors.New("error calling remote API")))
+				Expect(certProject).To(BeNil())
+			})
+		})
+	})
+})