From bf377c5ad5c05f0966ec5671b77ef8105c3c04dc Mon Sep 17 00:00:00 2001
From: Morten Torkildsen <mortent@google.com>
Date: Wed, 24 Apr 2019 20:07:22 -0700
Subject: [PATCH] fix(helm): Only validate new manifests

Signed-off-by: Morten Torkildsen <mortent@google.com>
---
 pkg/kube/client.go                         | 17 +++++++++++++++--
 pkg/tiller/environment/environment.go      | 17 +++++++++++++++++
 pkg/tiller/environment/environment_test.go |  3 +++
 pkg/tiller/release_server.go               |  3 +--
 pkg/tiller/release_server_test.go          |  3 +++
 5 files changed, 39 insertions(+), 4 deletions(-)

diff --git a/pkg/kube/client.go b/pkg/kube/client.go
index aa025eb0a..36467fad5 100644
--- a/pkg/kube/client.go
+++ b/pkg/kube/client.go
@@ -141,7 +141,7 @@ func (c *Client) validator() validation.Schema {
 	return schema
 }
 
-// BuildUnstructured validates for Kubernetes objects and returns unstructured infos.
+// BuildUnstructured reads Kubernetes objects and returns unstructured infos.
 func (c *Client) BuildUnstructured(namespace string, reader io.Reader) (Result, error) {
 	var result Result
 
@@ -150,13 +150,26 @@ func (c *Client) BuildUnstructured(namespace string, reader io.Reader) (Result,
 		ContinueOnError().
 		NamespaceParam(namespace).
 		DefaultNamespace().
-		Schema(c.validator()).
 		Stream(reader, "").
 		Flatten().
 		Do().Infos()
 	return result, scrubValidationError(err)
 }
 
+// Validate reads Kubernetes manifests and validates the content.
+func (c *Client) Validate(namespace string, reader io.Reader) error {
+	_, err := c.NewBuilder().
+		Unstructured().
+		ContinueOnError().
+		NamespaceParam(namespace).
+		DefaultNamespace().
+		Schema(c.validator()).
+		Stream(reader, "").
+		Flatten().
+		Do().Infos()
+	return scrubValidationError(err)
+}
+
 // Build validates for Kubernetes objects and returns resource Infos from a io.Reader.
 func (c *Client) Build(namespace string, reader io.Reader) (Result, error) {
 	var result Result
diff --git a/pkg/tiller/environment/environment.go b/pkg/tiller/environment/environment.go
index 21c23d421..24b93bfb3 100644
--- a/pkg/tiller/environment/environment.go
+++ b/pkg/tiller/environment/environment.go
@@ -147,8 +147,20 @@ type KubeClient interface {
 	UpdateWithOptions(namespace string, originalReader, modifiedReader io.Reader, opts kube.UpdateOptions) error
 
 	Build(namespace string, reader io.Reader) (kube.Result, error)
+
+	// BuildUnstructured reads a stream of manifests from a reader and turns them into
+	// info objects. Manifests are not validated against the schema, but it will fail if
+	// any resoures types are not known by the apiserver.
+	//
+	// reader must contain a YAML stream (one or more YAML documents separated by "\n---\n").
 	BuildUnstructured(namespace string, reader io.Reader) (kube.Result, error)
 
+	// Validate reads a stream of manifests from a reader and validates them against
+	// the schema from the apiserver. It returns an error if any of the manifests does not validate.
+	//
+	// reader must contain a YAML stream (one or more YAML documents separated by "\n---\n").
+	Validate(namespace string, reader io.Reader) error
+
 	// WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase
 	// and returns said phase (PodSucceeded or PodFailed qualify).
 	WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (v1.PodPhase, error)
@@ -214,6 +226,11 @@ func (p *PrintingKubeClient) BuildUnstructured(ns string, reader io.Reader) (kub
 	return []*resource.Info{}, nil
 }
 
+// Validate implements KubeClient Validate
+func (p *PrintingKubeClient) Validate(ns string, reader io.Reader) error {
+	return nil
+}
+
 // WaitAndGetCompletedPodPhase implements KubeClient WaitAndGetCompletedPodPhase.
 func (p *PrintingKubeClient) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (v1.PodPhase, error) {
 	_, err := io.Copy(p.Out, reader)
diff --git a/pkg/tiller/environment/environment_test.go b/pkg/tiller/environment/environment_test.go
index 24ff8b88d..1c2d5343f 100644
--- a/pkg/tiller/environment/environment_test.go
+++ b/pkg/tiller/environment/environment_test.go
@@ -64,6 +64,9 @@ func (k *mockKubeClient) Build(ns string, reader io.Reader) (kube.Result, error)
 func (k *mockKubeClient) BuildUnstructured(ns string, reader io.Reader) (kube.Result, error) {
 	return []*resource.Info{}, nil
 }
+func (k *mockKubeClient) Validate(ns string, reader io.Reader) error {
+	return nil
+}
 func (k *mockKubeClient) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (v1.PodPhase, error) {
 	return v1.PodUnknown, nil
 }
diff --git a/pkg/tiller/release_server.go b/pkg/tiller/release_server.go
index 6733035f7..c5638d20d 100644
--- a/pkg/tiller/release_server.go
+++ b/pkg/tiller/release_server.go
@@ -436,8 +436,7 @@ func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook strin
 
 func validateManifest(c environment.KubeClient, ns string, manifest []byte) error {
 	r := bytes.NewReader(manifest)
-	_, err := c.BuildUnstructured(ns, r)
-	return err
+	return c.Validate(ns, r)
 }
 
 func validateReleaseName(releaseName string) error {
diff --git a/pkg/tiller/release_server_test.go b/pkg/tiller/release_server_test.go
index 05b41be20..d70221ed1 100644
--- a/pkg/tiller/release_server_test.go
+++ b/pkg/tiller/release_server_test.go
@@ -650,6 +650,9 @@ func (kc *mockHooksKubeClient) Build(ns string, reader io.Reader) (kube.Result,
 func (kc *mockHooksKubeClient) BuildUnstructured(ns string, reader io.Reader) (kube.Result, error) {
 	return []*resource.Info{}, nil
 }
+func (kc *mockHooksKubeClient) Validate(ns string, reader io.Reader) error {
+	return nil
+}
 func (kc *mockHooksKubeClient) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (v1.PodPhase, error) {
 	return v1.PodUnknown, nil
 }
-- 
GitLab