Skip to content

Conversation

jmccormick2001
Copy link
Contributor

Description of the change:
this PR stubs out the Basic and OLM tests into the scorecard2 alpha design, it includes a unit test and a unit test data directory, it also includes a stubbed out scorecard test binary which will eventually execute the tests within an image, for now it just runs the stubbed out tests from the command line.

The stubbed out tests produce a sample ScorecardTestResult that has a Pass state, eventually this will be replaced by a real test implementation.

Motivation for the change:

part of the overall scorecard2 implementation

Comment on lines +29 to +30
r.Name = BasicCheckStatusTest
r.Description = "Custom Resource has a Status Block"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this cause competing name and description fields between the test image output and the test config file? We probably want one source of truth for each field in the --list output and actual result output.

We need to answer a fundamental question I think:

  • What guarantee, if any, should scorecard make that a test that claims a certain name is actually the test that the runner expects it to be?

Consider the scenario of a pipeline that implements a policy that says "require that 'basic-check-status' runs and passes. What stops someone from editing their config to use a different image for that test and having it output a result that says "basic-check-status" passed?

It seems like the only way for the runner to really be able to trust the results is for it to be able to trust the image and entrypoint, since it would be very difficult to impersonate an image registry and convince a pipeline cluster to run your image instead of the trusted image, especially if SHAs are used in the image reference.

So:

  1. Is this scenario a real use-case?
  2. Should we document that image reference and entrypoint are the fields that drive test policies and "name" and "description" are merely there for helpful context for the test?

Thoughts @shawn-hurley @dmesser @jmccormick2001?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the description in the config.yaml is probably redundant since its included into the ScorecardTestResult by the test itself, I'll remove that in a future PR. I suppose however, having it in the config file gives you the ability to customize the test description (e.g. Spanish translation). The name in the config file serves as the entrypoint so those match what the image is expecting or else the test image will not know what test to run.

I think documenting image/entrypoint fields makes sense (option 2). I was planning on doing the documentation in a separate PR/story currently.

I think the only way you can trust test results is to insure you are running a valid version of the test images or else you can spoof results, I'd consider that the domain of the pipeline/CI maintainers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the only way you can trust test results is to insure you are running a valid version of the test images or else you can spoof results, I'd consider that the domain of the pipeline/CI maintainers.

But what is the method by with the pipeline/CI maintainers know which images are being used? With the current design, the pipeline runs operator-sdk scorecard --bundle <bundle> and then the output shows the results. The image and entrypoint are never surfaced to the caller.

WDYT about having the config file contribute some of the fields to the result output and the test image log contributing the test result fields:

  • config.yaml contributes:

    • image
    • entrypoint
    • labels
    • name
    • description
  • test log contributes:

    • state (pass/fail/error)
    • errors
    • suggestions
    • log

So with this --list would output just fields from config.yaml and running the test would output fields from both.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--list currently works off of the config file only, I was going to keep it that way....when the integration to the test image is coded, I think we'll need to decide to pull the name/description from either the config file or the image itself, not sure at this point.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. I can see arguments for name and description come from either config or the log output. What I'm after is that we're crystal clear how that works, and we probably need to document it once we have a decision if it isn't immediately obvious.

logrus.SetOutput(validationLogOutput)
defer logrus.SetOutput(origOutput)

cfg.PackageManifest, cfg.Bundles, cfg.BundleErrors = manifests.GetManifestsDir(bundlePath)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could be wrong, but I think the bundlePath will look like this (from the OLM operator bundle enhancement proposal):

$ tree
/
├── manifests
│   ├── testbackup.crd.yaml
│   ├── testcluster.crd.yaml
│   ├── testoperator.v0.1.0.clusterserviceversion.yaml
│   └── testrestore.crd.yaml
└── metadata
    └── annotations.yaml

I think we need to find a different function that loads a single bundle from disk.

@estroz @kevinrizza @ecordell does something exist that turns a bundle directory like this into a registry.Bundle? I don't see anything here that seems to help, but I'm probably overlooking something.

If we have that, I think most of this file may go away. We could basically use registry.Bundle directly, possibly wrapping it to expose the CRs from the CSVs alm-examples.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function, which is called by an SQLPopulator from operator-registry, will return a single registry.Bundle if bundlePath is a bundle directory, i.e. len(cfg.Bundles) == 1.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@estroz Does that help us? It seems like we're actually after is something like loadBundle, but for that, it seems like we would pass /bundle/manifests and then /bundle/metadata would be ignored.

We probably also don't want all the logrus logging.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logging isn't great, and I have a PR upstream to configure it. For now turning it off when running GetManifestsDir should be enough. Do we care about metadata in this PR?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe not. Just trying to capture the potential issues I see and want to make sure we don't let tech debt creep in from the beginning. Totally fine by me to add a TODO comment and a follow-on task to address this later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a TODO around changing the get bundle API call, there is a story to handle the bundle image case so thats probably a good time to address this API.

)

func main() {
entrypoint := os.Args[1:]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking nit: It would be helpful for this program to print out the full list of test names it supports if argv[0] is empty or unknown. Maybe add a TODO comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree, I added a TODO to not forget it.

@@ -0,0 +1,63 @@
apiVersion: apiextensions.k8s.io/v1beta1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want the directory structure for this testdata to match the bundle directory format we expect?

testdata
└── bundle
    ├── manifests
    │   ├── cache.example.com_memcacheds_crd.yaml
    │   ├── memcached-operator.v0.0.1.clusterserviceversion.yaml
    │   └── memcached-operator.v0.0.1.clusterserviceversion.yaml.orig
    └── metadata
        └── annotations.yaml

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the testdata to use the new bundle format. I need to add a correct annotations.yaml file at some point, I was unsure of the format of that, figured it could be done in another PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sweet, just added a sample annotations.yaml file.

Comment on lines +30 to +34
type TestBundle struct {
BundleErrors []errors.ManifestResult
Bundles []*registry.Bundle
CRs []unstructured.Unstructured
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this struct look like we expect it to look once the API we want for parsing a bundle directory exists?

My guess is that something like the following will exist:

func LoadBundleFromDirectory(bundlePath string) (*registry.Bundle, error) { ... }

With that in mind, can we just pass a registry.Bundle to the tests?

For getting CRs, we could either wrap registry.Bundle in a new type that has a method for getting the CRs, or we can just have a helper function that returns the CRs given a registry.Bundle.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is related to the other convo here.

Again, totally fine to add a TODO to get back to this later so we're not blocked.

Just want to make sure the final thing we ship uses the new bundle format and has a straightforward/intuitive API.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a TODO to address the API changes, at that point I like the idea of adding a helper to get the CRs if that is not part of the new API we eventually use.

return cfg, fmt.Errorf("error in csv retrieval %s", err.Error())
}

almExamples := csv.ObjectMeta.Annotations["alm-examples"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check if csv.GetAnnotations() == nil.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, fixed.

Copy link
Member

@joelanford joelanford left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

@openshift-ci-robot openshift-ci-robot added the lgtm Indicates that a PR is ready to be merged. label Apr 15, 2020
@jmccormick2001 jmccormick2001 merged commit 5479d95 into operator-framework:master Apr 16, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm Indicates that a PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants