feat(pkg/kube): add openapi validation for k8s objects

Add back OpenAPI validation for kubernetes objects.

Fixes: #6382

Signed-off-by: Adam Reese <adam@reese.io>
pull/6599/head
Adam Reese 5 years ago
parent 5e2071caef
commit 572b92dc8a
No known key found for this signature in database
GPG Key ID: 06F35E60A7A18DD6

@ -44,7 +44,7 @@ func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent,
return err
}
resources, err := cfg.KubeClient.Build(bytes.NewBufferString(h.Manifest))
resources, err := cfg.KubeClient.Build(bytes.NewBufferString(h.Manifest), false)
if err != nil {
return errors.Wrapf(err, "unable to build kubernetes object for %s hook %s", hook, h.Path)
}
@ -111,7 +111,7 @@ func (x hookByWeight) Less(i, j int) bool {
// deleteHookByPolicy deletes a hook if the hook policy instructs it to
func (cfg *Configuration) deleteHookByPolicy(h *release.Hook, policy release.HookDeletePolicy) error {
if hookHasDeletePolicy(h, policy) {
resources, err := cfg.KubeClient.Build(bytes.NewBufferString(h.Manifest))
resources, err := cfg.KubeClient.Build(bytes.NewBufferString(h.Manifest), false)
if err != nil {
return errors.Wrapf(err, "unable to build kubernetes object for deleting hook %s", h.Path)
}

@ -110,7 +110,7 @@ func (i *Install) installCRDs(crds []*chart.File) error {
totalItems := []*resource.Info{}
for _, obj := range crds {
// Read in the resources
res, err := i.cfg.KubeClient.Build(bytes.NewBuffer(obj.Data))
res, err := i.cfg.KubeClient.Build(bytes.NewBuffer(obj.Data), false)
if err != nil {
return errors.Wrapf(err, "failed to install CRD %s", obj.Name)
}
@ -219,7 +219,7 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release.
// Mark this release as in-progress
rel.SetStatus(release.StatusPendingInstall, "Initial install underway")
resources, err := i.cfg.KubeClient.Build(bytes.NewBufferString(rel.Manifest))
resources, err := i.cfg.KubeClient.Build(bytes.NewBufferString(rel.Manifest), false)
if err != nil {
return nil, errors.Wrap(err, "unable to build kubernetes objects from release manifest")
}

@ -74,7 +74,7 @@ func (r *ReleaseTesting) Run(name string) error {
}
}
}
hooks, err := r.cfg.KubeClient.Build(bytes.NewBufferString(manifestsToDelete.String()))
hooks, err := r.cfg.KubeClient.Build(bytes.NewBufferString(manifestsToDelete.String()), false)
if err != nil {
return fmt.Errorf("unable to build test hooks: %v", err)
}

@ -140,11 +140,11 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas
return targetRelease, nil
}
current, err := r.cfg.KubeClient.Build(bytes.NewBufferString(currentRelease.Manifest))
current, err := r.cfg.KubeClient.Build(bytes.NewBufferString(currentRelease.Manifest), false)
if err != nil {
return targetRelease, errors.Wrap(err, "unable to build kubernetes objects from current release manifest")
}
target, err := r.cfg.KubeClient.Build(bytes.NewBufferString(targetRelease.Manifest))
target, err := r.cfg.KubeClient.Build(bytes.NewBufferString(targetRelease.Manifest), false)
if err != nil {
return targetRelease, errors.Wrap(err, "unable to build kubernetes objects from new release manifest")
}

@ -188,7 +188,7 @@ func (u *Uninstall) deleteRelease(rel *release.Release) (string, []error) {
for _, file := range filesToDelete {
builder.WriteString("\n---\n" + file.Content)
}
resources, err := u.cfg.KubeClient.Build(strings.NewReader(builder.String()))
resources, err := u.cfg.KubeClient.Build(strings.NewReader(builder.String()), false)
if err != nil {
return "", []error{errors.Wrap(err, "unable to build kubernetes objects for delete")}
}

@ -189,11 +189,11 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin
}
func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Release) (*release.Release, error) {
current, err := u.cfg.KubeClient.Build(bytes.NewBufferString(originalRelease.Manifest))
current, err := u.cfg.KubeClient.Build(bytes.NewBufferString(originalRelease.Manifest), false)
if err != nil {
return upgradedRelease, errors.Wrap(err, "unable to build kubernetes objects from current release manifest")
}
target, err := u.cfg.KubeClient.Build(bytes.NewBufferString(upgradedRelease.Manifest))
target, err := u.cfg.KubeClient.Build(bytes.NewBufferString(upgradedRelease.Manifest), true)
if err != nil {
return upgradedRelease, errors.Wrap(err, "unable to build kubernetes objects from new release manifest")
}
@ -372,7 +372,7 @@ func (u *Upgrade) reuseValues(chart *chart.Chart, current *release.Release, newV
}
func validateManifest(c kube.Interface, manifest []byte) error {
_, err := c.Build(bytes.NewReader(manifest))
_, err := c.Build(bytes.NewReader(manifest), true)
return err
}

@ -122,9 +122,14 @@ func (c *Client) newBuilder() *resource.Builder {
}
// Build validates for Kubernetes objects and returns unstructured infos.
func (c *Client) Build(reader io.Reader) (ResourceList, error) {
func (c *Client) Build(reader io.Reader, validate bool) (ResourceList, error) {
schema, err := c.Factory.Validator(validate)
if err != nil {
return nil, err
}
result, err := c.newBuilder().
Unstructured().
Schema(schema).
Stream(reader, "").
Do().Infos()
return result, scrubValidationError(err)

@ -153,11 +153,11 @@ func TestUpdate(t *testing.T) {
}
}),
}
first, err := c.Build(objBody(&listA))
first, err := c.Build(objBody(&listA), false)
if err != nil {
t.Fatal(err)
}
second, err := c.Build(objBody(&listB))
second, err := c.Build(objBody(&listB), false)
if err != nil {
t.Fatal(err)
}
@ -222,7 +222,7 @@ func TestBuild(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Test for an invalid manifest
infos, err := c.Build(tt.reader)
infos, err := c.Build(tt.reader, false)
if err != nil && !tt.err {
t.Errorf("Got error message when no error should have occurred: %v", err)
} else if err != nil && strings.Contains(err.Error(), "--validate=false") {
@ -266,7 +266,7 @@ func TestPerform(t *testing.T) {
}
c := newTestClient()
infos, err := c.Build(tt.reader)
infos, err := c.Build(tt.reader, false)
if err != nil && err.Error() != tt.errMessage {
t.Errorf("Error while building manifests: %v", err)
}
@ -289,7 +289,7 @@ func TestPerform(t *testing.T) {
func TestReal(t *testing.T) {
t.Skip("This is a live test, comment this line to run")
c := New(nil)
resources, err := c.Build(strings.NewReader(guestbookManifest))
resources, err := c.Build(strings.NewReader(guestbookManifest), false)
if err != nil {
t.Fatal(err)
}
@ -299,7 +299,7 @@ func TestReal(t *testing.T) {
testSvcEndpointManifest := testServiceManifest + "\n---\n" + testEndpointManifest
c = New(nil)
resources, err = c.Build(strings.NewReader(testSvcEndpointManifest))
resources, err = c.Build(strings.NewReader(testSvcEndpointManifest), false)
if err != nil {
t.Fatal(err)
}
@ -307,7 +307,7 @@ func TestReal(t *testing.T) {
t.Fatal(err)
}
resources, err = c.Build(strings.NewReader(testEndpointManifest))
resources, err = c.Build(strings.NewReader(testEndpointManifest), false)
if err != nil {
t.Fatal(err)
}
@ -316,7 +316,7 @@ func TestReal(t *testing.T) {
t.Fatal(errs)
}
resources, err = c.Build(strings.NewReader(testSvcEndpointManifest))
resources, err = c.Build(strings.NewReader(testSvcEndpointManifest), false)
if err != nil {
t.Fatal(err)
}

@ -83,11 +83,11 @@ func (f *FailingKubeClient) Update(r, modified kube.ResourceList, ignoreMe bool)
}
// Build returns the configured error if set or prints
func (f *FailingKubeClient) Build(r io.Reader) (kube.ResourceList, error) {
func (f *FailingKubeClient) Build(r io.Reader, _ bool) (kube.ResourceList, error) {
if f.BuildError != nil {
return []*resource.Info{}, f.BuildError
}
return f.PrintingKubeClient.Build(r)
return f.PrintingKubeClient.Build(r, false)
}
// WaitAndGetCompletedPodPhase returns the configured error if set or prints

@ -82,7 +82,7 @@ func (p *PrintingKubeClient) Update(_, modified kube.ResourceList, _ bool) (*kub
}
// Build implements KubeClient Build.
func (p *PrintingKubeClient) Build(_ io.Reader) (kube.ResourceList, error) {
func (p *PrintingKubeClient) Build(_ io.Reader, _ bool) (kube.ResourceList, error) {
return []*resource.Info{}, nil
}

@ -51,7 +51,9 @@ type Interface interface {
//
// reader must contain a YAML stream (one or more YAML documents separated
// by "\n---\n")
Build(reader io.Reader) (ResourceList, error)
//
// Validates against OpenAPI schema if validate is true.
Build(reader io.Reader, validate bool) (ResourceList, error)
// WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase
// and returns said phase (PodSucceeded or PodFailed qualify).

Loading…
Cancel
Save