Commit d10e9186 authored by Adam Reese's avatar Adam Reese

feat(tiller): validate objects against kube schema on dry-run

parent a95bd105
......@@ -50,8 +50,6 @@ var ErrNoObjectsVisited = goerrors.New("no objects visited")
// Client represents a client capable of communicating with the Kubernetes API.
type Client struct {
cmdutil.Factory
// Validate idicates whether to load a schema for validation.
Validate bool
// SchemaCacheDir is the path for loading cached schema.
SchemaCacheDir string
}
......@@ -60,7 +58,6 @@ type Client struct {
func New(config clientcmd.ClientConfig) *Client {
return &Client{
Factory: cmdutil.NewFactory(config),
Validate: true,
SchemaCacheDir: clientcmd.RecommendedSchemaFile,
}
}
......@@ -91,8 +88,8 @@ func (c *Client) Create(namespace string, reader io.Reader) error {
return perform(c, namespace, reader, createResource)
}
func (c *Client) newBuilder(namespace string, reader io.Reader) *resource.Builder {
schema, err := c.Validator(c.Validate, c.SchemaCacheDir)
func (c *Client) newBuilder(namespace string, reader io.Reader) *resource.Result {
schema, err := c.Validator(true, c.SchemaCacheDir)
if err != nil {
log.Printf("warning: failed to load schema: %s", err)
}
......@@ -102,7 +99,13 @@ func (c *Client) newBuilder(namespace string, reader io.Reader) *resource.Builde
NamespaceParam(namespace).
DefaultNamespace().
Stream(reader, "").
Flatten()
Flatten().
Do()
}
// Build validates for Kubernetes objects and returns resource Infos from a io.Reader.
func (c *Client) Build(namespace string, reader io.Reader) ([]*resource.Info, error) {
return c.newBuilder(namespace, reader).Infos()
}
// Get gets kubernetes resources as pretty printed string
......@@ -165,12 +168,12 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) {
//
// Namespace will set the namespaces
func (c *Client) Update(namespace string, currentReader, targetReader io.Reader, recreate bool) error {
currentInfos, err := c.newBuilder(namespace, currentReader).Do().Infos()
currentInfos, err := c.Build(namespace, currentReader)
if err != nil {
return fmt.Errorf("failed decoding reader into objects: %s", err)
}
target := c.newBuilder(namespace, targetReader).Do()
target := c.newBuilder(namespace, targetReader)
if target.Err() != nil {
return fmt.Errorf("failed decoding reader into objects: %s", target.Err())
}
......@@ -283,7 +286,7 @@ func (c *Client) WatchUntilReady(namespace string, reader io.Reader, timeout int
}
func perform(c *Client, namespace string, reader io.Reader, fn ResourceActorFunc) error {
infos, err := c.newBuilder(namespace, reader).Do().Infos()
infos, err := c.Build(namespace, reader)
switch {
case err != nil:
return scrubValidationError(err)
......
......@@ -31,6 +31,7 @@ import (
"k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/storage"
"k8s.io/helm/pkg/storage/driver"
"k8s.io/kubernetes/pkg/kubectl/resource"
)
// DefaultTillerNamespace is the default namespace for tiller.
......@@ -132,6 +133,8 @@ type KubeClient interface {
// reader must contain a YAML stream (one or more YAML documents separated
// by "\n---\n").
Update(namespace string, originalReader, modifiedReader io.Reader, recreate bool) error
Build(namespace string, reader io.Reader) ([]*resource.Info, error)
}
// PrintingKubeClient implements KubeClient, but simply prints the reader to
......@@ -172,6 +175,11 @@ func (p *PrintingKubeClient) Update(ns string, currentReader, modifiedReader io.
return err
}
// Build implements KubeClient Build.
func (p *PrintingKubeClient) Build(ns string, reader io.Reader) ([]*resource.Info, error) {
return []*resource.Info{}, nil
}
// Environment provides the context for executing a client request.
//
// All services in a context are concurrency safe.
......
......@@ -23,6 +23,7 @@ import (
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/kubernetes/pkg/kubectl/resource"
)
type mockEngine struct {
......@@ -50,6 +51,9 @@ func (k *mockKubeClient) Update(ns string, currentReader, modifiedReader io.Read
func (k *mockKubeClient) WatchUntilReady(ns string, r io.Reader, t int64) error {
return nil
}
func (k *mockKubeClient) Build(ns string, reader io.Reader) ([]*resource.Info, error) {
return []*resource.Info{}, nil
}
var _ Engine = &mockEngine{}
var _ KubeClient = &mockKubeClient{}
......
......@@ -435,7 +435,8 @@ func (s *ReleaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*rele
if len(notesTxt) > 0 {
updatedRelease.Info.Status.Notes = notesTxt
}
return currentRelease, updatedRelease, nil
err = validateManifest(s.env.KubeClient, currentRelease.Namespace, manifestDoc.Bytes())
return currentRelease, updatedRelease, err
}
// RollbackRelease rolls back to a previous version of the given release.
......@@ -706,7 +707,9 @@ func (s *ReleaseServer) prepareRelease(req *services.InstallReleaseRequest) (*re
if len(notesTxt) > 0 {
rel.Info.Status.Notes = notesTxt
}
return rel, nil
err = validateManifest(s.env.KubeClient, req.Namespace, manifestDoc.Bytes())
return rel, err
}
func getVersionSet(client discovery.ServerGroupsInterface) (versionSet, error) {
......@@ -1048,3 +1051,9 @@ func splitManifests(bigfile string) map[string]string {
}
return res
}
func validateManifest(c environment.KubeClient, ns string, manifest []byte) error {
r := bytes.NewReader(manifest)
_, err := c.Build(ns, r)
return err
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment