New validation logic for addon metadata can be added by creating a validator under the validator package. Please check however to see if the validation you are implementing already fits under an existing validator. In that case it would be appropriate to just extend the functionality of the exising validator instead of creating a new one.
First determine the unique code to assign to the validator. This code will
be selected based on the existing validators and any validators that are
still works in progress. The code should equal n+1 where n is the
current greatest validator code. Then choose a meaningful name and
description to describe the validator to end users as this is what
they will see when a validation result is returned to them.
Under the validator package create a subpackage named
for the unique code of your validator. For example, a validator with code
AM9999 will have a full import path of
github.com/mt-sre/addon-metadata-operator/pkg/validator/am9999.
Within this package you should have at least two .go files. One file
will implement the validator and the second will implement unit tests for
that validator.
Every validator must implement the validator.Validator interface:
type Validator interface {
Code() Code
Name() string
Description() string
Run(context.Context, types.MetaBundle) Result
}This is partially achieved by including a *validator.Base as a
member of your validator. However the Run method is up to you
to define. Luckily, the *validator.Base helps with that by
providing some helper functions
(Success, Fail, Error, RetryableError) that ensures you
return a proper validator.Result based on the logic of
your validator.
In addition to the validator itself your package must provide
an initializer type Initializer func(Dependencies) (Validator, error)
which injects dependencies from a validator.Runner instance
and either returns an initialized validator.Validator or an error.
This initializer must then be passed to the validator.Register function
which should be called at the top of your validator implementation file.
Once your validator is implemented a minimum of two tests are required.
One test should validate at least one valid types.MetaBundle that
will result in a success. A second test should do the same, but with
invalid bundles which produce a fail result. The
testutils package provides some helpers
to assist with this testing. Calling testutils.DefaultValidBundleMap
will provide bundles which should always pass validation. Additionally,
calling testutils.NewValidatorTester will initialize your validator
with configuratble dependencies and offers two methods,
TestValidBundles and TestInvalidBundles which both accept a map
of descriptive names to bundles and ensure they are valid or invalid
respectively.
Once your validator is ready it must be imported within the
register package as a blank import.
Any packages which then import the register package will register
the included validators with a validator.Runner instance. This makes
it possible to isolate side-effectual imports during testing or other
unwanted scenarios.
Unit tests can be run using ./mage test:unit and the go test command
can be used to run specific tests directly.