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 return err
} }
resources, err := cfg.KubeClient.Build(bytes.NewBufferString(h.Manifest)) resources, err := cfg.KubeClient.Build(bytes.NewBufferString(h.Manifest), false)
if err != nil { if err != nil {
return errors.Wrapf(err, "unable to build kubernetes object for %s hook %s", hook, h.Path) 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 // deleteHookByPolicy deletes a hook if the hook policy instructs it to
func (cfg *Configuration) deleteHookByPolicy(h *release.Hook, policy release.HookDeletePolicy) error { func (cfg *Configuration) deleteHookByPolicy(h *release.Hook, policy release.HookDeletePolicy) error {
if hookHasDeletePolicy(h, policy) { 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 { if err != nil {
return errors.Wrapf(err, "unable to build kubernetes object for deleting hook %s", h.Path) 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{} totalItems := []*resource.Info{}
for _, obj := range crds { for _, obj := range crds {
// Read in the resources // 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 { if err != nil {
return errors.Wrapf(err, "failed to install CRD %s", obj.Name) 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 // Mark this release as in-progress
rel.SetStatus(release.StatusPendingInstall, "Initial install underway") 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 { if err != nil {
return nil, errors.Wrap(err, "unable to build kubernetes objects from release manifest") 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 { if err != nil {
return fmt.Errorf("unable to build test hooks: %v", err) 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 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 { if err != nil {
return targetRelease, errors.Wrap(err, "unable to build kubernetes objects from current release manifest") 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 { if err != nil {
return targetRelease, errors.Wrap(err, "unable to build kubernetes objects from new release manifest") 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 { for _, file := range filesToDelete {
builder.WriteString("\n---\n" + file.Content) 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 { if err != nil {
return "", []error{errors.Wrap(err, "unable to build kubernetes objects for delete")} 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) { 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 { if err != nil {
return upgradedRelease, errors.Wrap(err, "unable to build kubernetes objects from current release manifest") 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 { if err != nil {
return upgradedRelease, errors.Wrap(err, "unable to build kubernetes objects from new release manifest") 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 { func validateManifest(c kube.Interface, manifest []byte) error {
_, err := c.Build(bytes.NewReader(manifest)) _, err := c.Build(bytes.NewReader(manifest), true)
return err return err
} }

@ -122,9 +122,14 @@ func (c *Client) newBuilder() *resource.Builder {
} }
// Build validates for Kubernetes objects and returns unstructured infos. // 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(). result, err := c.newBuilder().
Unstructured(). Unstructured().
Schema(schema).
Stream(reader, ""). Stream(reader, "").
Do().Infos() Do().Infos()
return result, scrubValidationError(err) 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
second, err := c.Build(objBody(&listB)) second, err := c.Build(objBody(&listB), false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -222,7 +222,7 @@ func TestBuild(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
// Test for an invalid manifest // Test for an invalid manifest
infos, err := c.Build(tt.reader) infos, err := c.Build(tt.reader, false)
if err != nil && !tt.err { if err != nil && !tt.err {
t.Errorf("Got error message when no error should have occurred: %v", err) t.Errorf("Got error message when no error should have occurred: %v", err)
} else if err != nil && strings.Contains(err.Error(), "--validate=false") { } else if err != nil && strings.Contains(err.Error(), "--validate=false") {
@ -266,7 +266,7 @@ func TestPerform(t *testing.T) {
} }
c := newTestClient() c := newTestClient()
infos, err := c.Build(tt.reader) infos, err := c.Build(tt.reader, false)
if err != nil && err.Error() != tt.errMessage { if err != nil && err.Error() != tt.errMessage {
t.Errorf("Error while building manifests: %v", err) t.Errorf("Error while building manifests: %v", err)
} }
@ -289,7 +289,7 @@ func TestPerform(t *testing.T) {
func TestReal(t *testing.T) { func TestReal(t *testing.T) {
t.Skip("This is a live test, comment this line to run") t.Skip("This is a live test, comment this line to run")
c := New(nil) c := New(nil)
resources, err := c.Build(strings.NewReader(guestbookManifest)) resources, err := c.Build(strings.NewReader(guestbookManifest), false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -299,7 +299,7 @@ func TestReal(t *testing.T) {
testSvcEndpointManifest := testServiceManifest + "\n---\n" + testEndpointManifest testSvcEndpointManifest := testServiceManifest + "\n---\n" + testEndpointManifest
c = New(nil) c = New(nil)
resources, err = c.Build(strings.NewReader(testSvcEndpointManifest)) resources, err = c.Build(strings.NewReader(testSvcEndpointManifest), false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -307,7 +307,7 @@ func TestReal(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
resources, err = c.Build(strings.NewReader(testEndpointManifest)) resources, err = c.Build(strings.NewReader(testEndpointManifest), false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -316,7 +316,7 @@ func TestReal(t *testing.T) {
t.Fatal(errs) t.Fatal(errs)
} }
resources, err = c.Build(strings.NewReader(testSvcEndpointManifest)) resources, err = c.Build(strings.NewReader(testSvcEndpointManifest), false)
if err != nil { if err != nil {
t.Fatal(err) 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 // 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 { if f.BuildError != nil {
return []*resource.Info{}, f.BuildError 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 // 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. // 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 return []*resource.Info{}, nil
} }

@ -51,7 +51,9 @@ type Interface interface {
// //
// reader must contain a YAML stream (one or more YAML documents separated // reader must contain a YAML stream (one or more YAML documents separated
// by "\n---\n") // 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 // WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase
// and returns said phase (PodSucceeded or PodFailed qualify). // and returns said phase (PodSucceeded or PodFailed qualify).

Loading…
Cancel
Save