Repository: redhat-openshift-ecosystem/openshift-preflight
Pull request #473 (2022-03-10 15:56:57)

adding in createArtifacts method so we can send pyxis the preflight.log
- Fixes: #447 
- Added `POST` call to `/artifacts`
- Added code to read log file off of disk and build `Artifact` struct
- **Note** Existing tests already cover this method since the URL path is shared. Tests of pyxis code will be enhanced when we we switch to grahpql calls.

Signed-off-by: Adam D. Cornett <adc@redhat.com>

openshift-ci[bot]: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/redhat-openshift-ecosystem/openshift-preflight/pull/473#" title="Author self-approved">acornett21</a>*, *<a href="https://github.com/redhat-openshift-ecosystem/openshift-preflight/pull/473#pullrequestreview-906668116" title="LGTM">bcrochet</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=redhat-openshift-ecosystem%2Fopenshift-preflight).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/redhat-openshift-ecosystem/openshift-preflight/blob/main/OWNERS)~~ [acornett21,bcrochet]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index ac4dcbb..2354ecf 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -27,8 +27,8 @@ jobs:
     - name: Format
       run: make fmt
 
-    - name: Build Multi-arch
-      run: make build-multi-arch
-
     - name: Test
       run: make test
+
+    - name: Build Multi-arch
+      run: make build-multi-arch
diff --git a/certification/pyxis/pyxis.go b/certification/pyxis/pyxis.go
index b8a756e..37c316f 100644
--- a/certification/pyxis/pyxis.go
+++ b/certification/pyxis/pyxis.go
@@ -328,6 +328,48 @@ func (p *pyxisEngine) createTestResults(ctx context.Context, testResults *TestRe
 	return &newTestResults, nil
 }
 
+func (p *pyxisEngine) createArtifact(ctx context.Context, artifact *Artifact) (*Artifact, error) {
+	b, err := json.Marshal(artifact)
+	if err != nil {
+		log.Error(err)
+		return nil, err
+	}
+	req, err := p.newRequestWithApiToken(ctx, http.MethodPost, getPyxisUrl(fmt.Sprintf("projects/certification/id/%s/artifacts", p.ProjectId)), bytes.NewReader(b))
+	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 createArtifact", string(body))
+		return nil, errors.ErrNon200StatusCode
+	}
+
+	var newArtifact Artifact
+	if err := json.Unmarshal(body, &newArtifact); err != nil {
+		log.Error(err)
+		return nil, err
+	}
+
+	return &newArtifact, nil
+}
+
 func (p *pyxisEngine) newRequestWithApiToken(ctx context.Context, method string, url string, body io.Reader) (*http.Request, error) {
 	req, err := http.NewRequestWithContext(ctx, method, url, body)
 	if err != nil {
diff --git a/certification/pyxis/submit.go b/certification/pyxis/submit.go
index a41be9b..a7a57c0 100644
--- a/certification/pyxis/submit.go
+++ b/certification/pyxis/submit.go
@@ -8,7 +8,7 @@ import (
 	log "github.com/sirupsen/logrus"
 )
 
-func (p *pyxisEngine) SubmitResults(ctx context.Context, certProject *CertProject, certImage *CertImage, rpmManifest *RPMManifest, testResults *TestResults) (*CertProject, *CertImage, *TestResults, error) {
+func (p *pyxisEngine) SubmitResults(ctx context.Context, certProject *CertProject, certImage *CertImage, rpmManifest *RPMManifest, testResults *TestResults, artifacts []Artifact) (*CertProject, *CertImage, *TestResults, error) {
 	var err error
 
 	if certProject.CertificationStatus == "Started" {
@@ -57,6 +57,14 @@ func (p *pyxisEngine) SubmitResults(ctx context.Context, certProject *CertProjec
 		}
 	}
 
+	for _, artifact := range artifacts {
+		artifact.ImageID = certImage.ID
+		if _, err := p.createArtifact(ctx, &artifact); err != nil {
+			log.Errorf("%s: could not create artifact: %s", err, artifact.Filename)
+			return nil, nil, nil, err
+		}
+	}
+
 	testResults.ImageID = certImage.ID
 	testResults, err = p.createTestResults(ctx, testResults)
 	if err != nil {
diff --git a/certification/pyxis/types.go b/certification/pyxis/types.go
index 1b36df0..7bb75bd 100644
--- a/certification/pyxis/types.go
+++ b/certification/pyxis/types.go
@@ -1,6 +1,8 @@
 package pyxis
 
-import "github.com/redhat-openshift-ecosystem/openshift-preflight/certification/formatters"
+import (
+	"github.com/redhat-openshift-ecosystem/openshift-preflight/certification/formatters"
+)
 
 type CertImage struct {
 	ID                     string       `json:"_id,omitempty"`
@@ -99,3 +101,13 @@ type TestResults struct {
 	ImageID     string `json:"image_id,omitempty"`
 	formatters.UserResponse
 }
+
+type Artifact struct {
+	ID          string `json:"_id"`
+	CertProject string `json:"cert_project"`
+	Content     string `json:"content"`
+	ContentType string `json:"content_type"`
+	FileSize    int64  `json:"file_size"`
+	Filename    string `json:"filename"`
+	ImageID     string `json:"image_id"`
+}
diff --git a/cmd/check_container.go b/cmd/check_container.go
index d77eb4b..c9cb33c 100644
--- a/cmd/check_container.go
+++ b/cmd/check_container.go
@@ -212,7 +212,36 @@ var checkContainerCmd = &cobra.Command{
 				return err
 			}
 
-			_, certImage, _, err = pyxisEngine.SubmitResults(ctx, certProject, certImage, rpmManifest, testResults)
+			logFileName := viper.GetString("logfile")
+
+			logFile, err := os.Open(logFileName)
+			if err != nil {
+				return err
+			}
+			defer logFile.Close()
+
+			logFileBytes, err := io.ReadAll(logFile)
+			if err != nil {
+				return err
+			}
+
+			logFileInfo, err := logFile.Stat()
+			if err != nil {
+				return err
+			}
+
+			logFileArtifact := pyxis.Artifact{
+				CertProject: projectId,
+				Content:     string(logFileBytes),
+				ContentType: http.DetectContentType(logFileBytes),
+				Filename:    logFileName,
+				FileSize:    logFileInfo.Size(),
+			}
+
+			artifacts := make([]pyxis.Artifact, 0, 1)
+			artifacts = append(artifacts, logFileArtifact)
+
+			_, certImage, _, err = pyxisEngine.SubmitResults(ctx, certProject, certImage, rpmManifest, testResults, artifacts)
 			if err != nil {
 				return err
 			}


Test patch:
diff --git a/certification/pyxis/submit_test.go b/certification/pyxis/submit_test.go
index e92a9b5..af01e63 100644
--- a/certification/pyxis/submit_test.go
+++ b/certification/pyxis/submit_test.go
@@ -20,7 +20,7 @@ var _ = Describe("Pyxis Submit", func() {
 	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(ctx, &CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{})
+				certProject, certImage, testResults, err := pyxisEngine.SubmitResults(ctx, &CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{}, []Artifact{})
 				Expect(err).ToNot(HaveOccurred())
 				Expect(certProject).ToNot(BeNil())
 				Expect(certImage).ToNot(BeNil())
@@ -39,7 +39,7 @@ var _ = Describe("Pyxis Submit updateProject 401 Unauthorized", func() {
 	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(ctx, &CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{})
+				certProject, certImage, testResults, err := pyxisEngine.SubmitResults(ctx, &CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{}, []Artifact{})
 				Expect(err).To(MatchError(fmt.Errorf("%w: %s", errors.New("error calling remote API"), "could not retrieve project")))
 				Expect(certProject).To(BeNil())
 				Expect(certImage).To(BeNil())
@@ -58,7 +58,7 @@ var _ = Describe("Pyxis Submit with createImage 409 Conflict", func() {
 	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(ctx, &CertProject{}, &CertImage{}, &RPMManifest{}, &TestResults{})
+				certProject, certImage, testResults, err := pyxisEngine.SubmitResults(ctx, &CertProject{}, &CertImage{}, &RPMManifest{}, &TestResults{}, []Artifact{})
 				Expect(err).ToNot(HaveOccurred())
 				Expect(certProject).ToNot(BeNil())
 				Expect(certImage).ToNot(BeNil())
@@ -77,7 +77,7 @@ var _ = Describe("Pyxis Submit with createImage 401 Unauthorized", func() {
 	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(ctx, &CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{})
+				certProject, certImage, testResults, err := pyxisEngine.SubmitResults(ctx, &CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{}, []Artifact{})
 				Expect(err).To(MatchError(errors.New("error calling remote API")))
 				Expect(certProject).To(BeNil())
 				Expect(certImage).To(BeNil())
@@ -96,7 +96,7 @@ var _ = Describe("Pyxis Submit with createImage 409 Conflict and getImage 401 Un
 	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(ctx, &CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{})
+				certProject, certImage, testResults, err := pyxisEngine.SubmitResults(ctx, &CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{}, []Artifact{})
 				Expect(err).To(MatchError(errors.New("error calling remote API")))
 				Expect(certProject).To(BeNil())
 				Expect(certImage).To(BeNil())
@@ -115,7 +115,7 @@ var _ = Describe("Pyxis Submit with createRPMManifest 409 Conflict", func() {
 	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(ctx, &CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{})
+				certProject, certImage, testResults, err := pyxisEngine.SubmitResults(ctx, &CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{}, []Artifact{})
 				Expect(err).ToNot(HaveOccurred())
 				Expect(certProject).ToNot(BeNil())
 				Expect(certImage).ToNot(BeNil())
@@ -134,7 +134,7 @@ var _ = Describe("Pyxis Submit with createRPMManifest 401 Unauthorized", func()
 	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(ctx, &CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{})
+				certProject, certImage, testResults, err := pyxisEngine.SubmitResults(ctx, &CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{}, []Artifact{})
 				Expect(err).To(MatchError(errors.New("error calling remote API")))
 				Expect(certProject).To(BeNil())
 				Expect(certImage).To(BeNil())
@@ -153,7 +153,7 @@ var _ = Describe("Pyxis Submit with createRPMManifest 409 Conflict and getRPMMan
 	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(ctx, &CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{})
+				certProject, certImage, testResults, err := pyxisEngine.SubmitResults(ctx, &CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{}, []Artifact{})
 				Expect(err).To(MatchError(errors.New("error calling remote API")))
 				Expect(certProject).To(BeNil())
 				Expect(certImage).To(BeNil())
@@ -172,7 +172,7 @@ var _ = Describe("Pyxis Submit with createTestResults 401 Unauthorized", func()
 	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(ctx, &CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{})
+				certProject, certImage, testResults, err := pyxisEngine.SubmitResults(ctx, &CertProject{CertificationStatus: "Started"}, &CertImage{}, &RPMManifest{}, &TestResults{}, []Artifact{})
 				Expect(err).To(MatchError(errors.New("error calling remote API")))
 				Expect(certProject).To(BeNil())
 				Expect(certImage).To(BeNil())