Skip to content

Commit

Permalink
Add lib to preflight
Browse files Browse the repository at this point in the history
Testing the lib

Switch back to old repo name

Dep updates

Operator Framework update

lock otel

more dep updates

Dep update

update deps

Add NewManualConfig

Add NewManualOperatorConfig

Rename back
  • Loading branch information
sebrandon1 committed Oct 20, 2022
1 parent a41492b commit 929fb63
Show file tree
Hide file tree
Showing 17 changed files with 616 additions and 527 deletions.
22 changes: 22 additions & 0 deletions certification/runtime/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,28 @@ func NewConfigFrom(vcfg viper.Viper) (*Config, error) {
return &cfg, nil
}

func NewManualContainerConfig(image, responseFormat, artifactsDir string, submit, writeJUnit bool) *Config {
return &Config{
Image: image,
Submit: submit,
WriteJUnit: writeJUnit,
ResponseFormat: responseFormat,
Artifacts: artifactsDir,
}
}

func NewManualOperatorConfig(image, responseFormat, artifactsDir string, writeJUnit bool) *Config {
return &Config{
Image: image,
Submit: false, // operator results are not submitted
WriteJUnit: writeJUnit,
ResponseFormat: responseFormat,
Artifacts: artifactsDir,
Bundle: false,
Scratch: false,
}
}

// storeContainerPolicyConfiguration reads container-policy-specific config
// items in viper, normalizes them, and stores them in Config.
func (c *Config) storeContainerPolicyConfiguration(vcfg viper.Viper) {
Expand Down
20 changes: 0 additions & 20 deletions cmd/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package cmd
import (
"bytes"
"context"
"fmt"
"strings"

"github.com/redhat-openshift-ecosystem/openshift-preflight/certification/artifacts"
Expand Down Expand Up @@ -63,25 +62,6 @@ func resultsFilenameWithExtension(ext string) string {
return strings.Join([]string{"results", ext}, ".")
}

func buildConnectURL(projectID string) string {
connectURL := fmt.Sprintf("https://connect.redhat.com/projects/%s", projectID)

pyxisEnv := viper.GetString("pyxis_env")
if len(pyxisEnv) > 0 && pyxisEnv != "prod" {
connectURL = fmt.Sprintf("https://connect.%s.redhat.com/projects/%s", viper.GetString("pyxis_env"), projectID)
}

return connectURL
}

func buildOverviewURL(projectID string) string {
return fmt.Sprintf("%s/overview", buildConnectURL(projectID))
}

func buildScanResultsURL(projectID string, imageID string) string {
return fmt.Sprintf("%s/images/%s/scan-results", buildConnectURL(projectID), imageID)
}

func convertPassedOverall(passedOverall bool) string {
if passedOverall {
return "PASSED"
Expand Down
106 changes: 9 additions & 97 deletions cmd/check_container.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package cmd

import (
"context"
"fmt"
"strings"

"github.com/redhat-openshift-ecosystem/openshift-preflight/certification"
"github.com/redhat-openshift-ecosystem/openshift-preflight/certification/engine"
"github.com/redhat-openshift-ecosystem/openshift-preflight/certification/formatters"
"github.com/redhat-openshift-ecosystem/openshift-preflight/certification/policy"
"github.com/redhat-openshift-ecosystem/openshift-preflight/certification/runtime"
"github.com/redhat-openshift-ecosystem/openshift-preflight/lib"
"github.com/redhat-openshift-ecosystem/openshift-preflight/version"

log "github.com/sirupsen/logrus"
Expand Down Expand Up @@ -52,53 +50,6 @@ func checkContainerCmd() *cobra.Command {
return checkContainerCmd
}

// checkContainerRunner contains all of the components necessary to run checkContainer.
type checkContainerRunner struct {
cfg *runtime.Config
pc pyxisClient
eng engine.CheckEngine
formatter formatters.ResponseFormatter
rw resultWriter
rs resultSubmitter
}

func newCheckContainerRunner(ctx context.Context, cfg *runtime.Config) (*checkContainerRunner, error) {
cfg.Policy = policy.PolicyContainer
cfg.Submit = submit

pyxisClient := newPyxisClient(ctx, cfg.ReadOnly())
// If we have a pyxisClient, we can query for container policy exceptions.
if pyxisClient != nil {
policy, err := getContainerPolicyExceptions(ctx, pyxisClient)
if err != nil {
return nil, err
}

cfg.Policy = policy
}

engine, err := engine.NewForConfig(ctx, cfg.ReadOnly())
if err != nil {
return nil, err
}

fmttr, err := formatters.NewForConfig(cfg.ReadOnly())
if err != nil {
return nil, err
}

rs := resolveSubmitter(pyxisClient, cfg.ReadOnly())

return &checkContainerRunner{
cfg: cfg,
pc: pyxisClient,
eng: engine,
formatter: fmttr,
rw: &runtime.ResultWriterFile{},
rs: rs,
}, nil
}

// checkContainerRunE executes checkContainer using the user args to inform the execution.
func checkContainerRunE(cmd *cobra.Command, args []string) error {
log.Info("certification library version ", version.Version.String())
Expand All @@ -114,62 +65,23 @@ func checkContainerRunE(cmd *cobra.Command, args []string) error {
cfg.Image = containerImage
cfg.ResponseFormat = formatters.DefaultFormat

checkContainer, err := newCheckContainerRunner(ctx, cfg)
checkContainer, err := lib.NewCheckContainerRunner(ctx, cfg, submit)
if err != nil {
return err
}

// Run the container check.
cmd.SilenceUsage = true
return preflightCheck(ctx,
checkContainer.cfg,
checkContainer.pc,
checkContainer.eng,
checkContainer.formatter,
checkContainer.rw,
checkContainer.rs,
return lib.PreflightCheck(ctx,
checkContainer.Cfg,
checkContainer.Pc,
checkContainer.Eng,
checkContainer.Formatter,
checkContainer.Rw,
checkContainer.Rs,
)
}

// resolveSubmitter will build out a resultSubmitter if the provided pyxisClient, pc, is not nil.
// The pyxisClient is a required component of the submitter. If pc is nil, then a noop submitter
// is returned instead, which does nothing.
func resolveSubmitter(pc pyxisClient, cfg certification.Config) resultSubmitter {
if pc != nil {
return &containerCertificationSubmitter{
certificationProjectID: cfg.CertificationProjectID(),
pyxis: pc,
dockerConfig: cfg.DockerConfig(),
preflightLogFile: cfg.LogFile(),
}
}

return &noopSubmitter{emitLog: true}
}

// getContainerPolicyExceptions will query Pyxis to determine if
// a given project has a certification excemptions, such as root or scratch.
// This will then return the corresponding policy.
//
// If no policy exception flags are found on the project, the standard
// container policy is returned.
func getContainerPolicyExceptions(ctx context.Context, pc pyxisClient) (policy.Policy, error) {
certProject, err := pc.GetProject(ctx)
if err != nil {
return "", fmt.Errorf("could not retrieve project: %w", err)
}
log.Debugf("Certification project name is: %s", certProject.Name)
if certProject.Container.Type == "scratch" {
return policy.PolicyScratch, nil
}

// if a partner sets `Host Level Access` in connect to `Privileged`, enable RootExceptionContainerPolicy checks
if certProject.Container.Privileged {
return policy.PolicyRoot, nil
}
return policy.PolicyContainer, nil
}

func checkContainerPositionalArgs(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("a container image positional argument is required")
Expand Down
163 changes: 163 additions & 0 deletions cmd/check_container_cmd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package cmd

import (
"bytes"
"context"
"os"
"path/filepath"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/redhat-openshift-ecosystem/openshift-preflight/certification/runtime"
"github.com/redhat-openshift-ecosystem/openshift-preflight/lib"
"github.com/spf13/viper"
)

var _ = Describe("Check Container Command", func() {
BeforeEach(createAndCleanupDirForArtifactsAndLogs)

Context("when running the check container subcommand", func() {
Context("With all of the required parameters", func() {
It("should reach the core logic, but throw an error because of the placeholder values for the container image", func() {
_, err := executeCommand(checkContainerCmd(), "example.com/example/image:mytag")
Expect(err).To(HaveOccurred())
})
})
})

Context("When validating check container arguments and flags", func() {
Context("and the user provided more than 1 positional arg", func() {
It("should fail to run", func() {
_, err := executeCommand(checkContainerCmd(), "foo", "bar")
Expect(err).To(HaveOccurred())
})
})

Context("and the user provided less than 1 positional arg", func() {
It("should fail to run", func() {
_, err := executeCommand(checkContainerCmd())
Expect(err).To(HaveOccurred())
})
})

DescribeTable("and the user has enabled the submit flag",
func(errString string, args []string) {
out, err := executeCommand(checkContainerCmd(), args...)
Expect(err).To(HaveOccurred())
Expect(out).To(ContainSubstring(errString))
},
Entry("certification-project-id and pyxis-api-token are not supplied", "certification Project ID must be specified when --submit is present", []string{"--submit", "foo"}),
Entry("pyxis-api-token is not supplied", "pyxis API Token must be specified when --submit is present", []string{"foo", "--submit", "--certification-project-id=fooid"}),
Entry("certification-project-id is not supplied", "certification Project ID must be specified when --submit is present", []string{"--submit", "foo", "--pyxis-api-token=footoken"}),
Entry("pyxis-api-token flag is present but empty because of '='", "cannot be empty when --submit is present", []string{"foo", "--submit", "--certification-project-id=fooid", "--pyxis-api-token="}),
Entry("certification-project-id flag is present but empty because of '='", "cannot be empty when --submit is present", []string{"foo", "--submit", "--certification-project-id=", "--pyxis-api-token=footoken"}),
Entry("submit is passed after empty api token", "pyxis API token and certification ID are required when --submit is present", []string{"foo", "--certification-project-id=fooid", "--pyxis-api-token", "--submit"}),
Entry("submit is passed with explicit value after empty api token", "pyxis API token and certification ID are required when --submit is present", []string{"foo", "--certification-project-id=fooid", "--pyxis-api-token", "--submit=true"}),
)

When("the user enables the submit flag", func() {
When("environment variables are used for certification ID and api token", func() {
BeforeEach(func() {
os.Setenv("PFLT_CERTIFICATION_PROJECT_ID", "certid")
os.Setenv("PFLT_PYXIS_API_TOKEN", "tokenid")
DeferCleanup(os.Unsetenv, "PFLT_CERTIFICATION_PROJECT_ID")
DeferCleanup(os.Unsetenv, "PFLT_PYXIS_API_TOKEN")
})
It("should still execute with no error", func() {
submit = true

err := checkContainerPositionalArgs(checkContainerCmd(), []string{"foo"})
Expect(err).ToNot(HaveOccurred())
Expect(viper.GetString("pyxis_api_token")).To(Equal("tokenid"))
Expect(viper.GetString("certification_project_id")).To(Equal("certid"))
})
})
When("a config file is used", func() {
BeforeEach(func() {
config := `pyxis_api_token: mytoken
certification_project_id: mycertid`
tempDir, err := os.MkdirTemp("", "check-container-submit-*")
Expect(err).ToNot(HaveOccurred())
err = os.WriteFile(filepath.Join(tempDir, "config.yaml"), bytes.NewBufferString(config).Bytes(), 0o644)
Expect(err).ToNot(HaveOccurred())
viper.AddConfigPath(tempDir)
DeferCleanup(os.RemoveAll, tempDir)
})
It("should still execute with no error", func() {
// Make sure that we've read the config file
initConfig()
submit = true

err := checkContainerPositionalArgs(checkContainerCmd(), []string{"foo"})
Expect(err).ToNot(HaveOccurred())
Expect(viper.GetString("pyxis_api_token")).To(Equal("mytoken"))
Expect(viper.GetString("certification_project_id")).To(Equal("mycertid"))
})
})
})
})

Context("When validating the certification-project-id flag", func() {
Context("and the flag is set properly", func() {
BeforeEach(func() {
viper.Set("certification_project_id", "123456789")
})
It("should not change the flag value", func() {
err := validateCertificationProjectID(checkContainerCmd(), []string{"foo"})
Expect(err).ToNot(HaveOccurred())
Expect(viper.GetString("certification_project_id")).To(Equal("123456789"))
})
})
Context("and a valid ospid format is provided", func() {
BeforeEach(func() {
viper.Set("certification_project_id", "ospid-123456789")
})
It("should strip ospid- from the flag value", func() {
err := validateCertificationProjectID(checkContainerCmd(), []string{"foo"})
Expect(err).ToNot(HaveOccurred())
Expect(viper.GetString("certification_project_id")).To(Equal("123456789"))
})
})
Context("and a legacy format with ospid is provided", func() {
BeforeEach(func() {
viper.Set("certification_project_id", "ospid-62423-f26c346-6cc1dc7fae92")
})
It("should throw an error", func() {
err := validateCertificationProjectID(checkContainerCmd(), []string{"foo"})
Expect(err).To(HaveOccurred())
})
})
Context("and a legacy format without ospid is provided", func() {
BeforeEach(func() {
viper.Set("certification_project_id", "62423-f26c346-6cc1dc7fae92")
})
It("should throw an error", func() {
err := validateCertificationProjectID(checkContainerCmd(), []string{"foo"})
Expect(err).To(HaveOccurred())
})
})
})

Context("When instantiating a checkContainerRunner", func() {
var cfg *runtime.Config

Context("and the user did not pass the submit flag", func() {
var origSubmitValue bool
BeforeEach(func() {
origSubmitValue = submit
submit = false
})

AfterEach(func() {
submit = origSubmitValue
})
It("should return a noopSubmitter resultSubmitter", func() {
runner, err := lib.NewCheckContainerRunner(context.TODO(), cfg, false)
Expect(err).ToNot(HaveOccurred())
_, rsIsCorrectType := runner.Rs.(*lib.NoopSubmitter)
Expect(rsIsCorrectType).To(BeTrue())
})
})

})
})
Loading

0 comments on commit 929fb63

Please sign in to comment.