diff --git a/Gopkg.lock b/Gopkg.lock
index 2d315d0b6be848952b949a0b77a2fd86a7e05061..e7086cda4010cb51643790236987f834c168d298 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -224,12 +224,15 @@
 [[projects]]
   name = "github.com/ksonnet/ksonnet-lib"
   packages = [
+    "ksonnet-gen/astext",
     "ksonnet-gen/jsonnet",
     "ksonnet-gen/ksonnet",
     "ksonnet-gen/kubespec",
-    "ksonnet-gen/kubeversion"
+    "ksonnet-gen/kubeversion",
+    "ksonnet-gen/nodemaker",
+    "ksonnet-gen/printer"
   ]
-  revision = "e8077023fa84395ad7976fa750136b39d0aa5f08"
+  revision = "b13dc1c505011ee838ae45324994dac432233000"
 
 [[projects]]
   branch = "master"
@@ -600,6 +603,6 @@
 [solve-meta]
   analyzer-name = "dep"
   analyzer-version = 1
-  inputs-digest = "2d8337f0b0870fb8b2c0a041d7b5e3e77b66b414fe9c2d7962ee5c5842bb94e0"
+  inputs-digest = "f44fb422720ed1d7e1a654a9c4b6f6d9baa90492bb28c459bf30552527255e83"
   solver-name = "gps-cdcl"
   solver-version = 1
diff --git a/Gopkg.toml b/Gopkg.toml
index 9e1c3e280ddfb95ef4fb670756682696b3a15a93..5899b546977d044a16cb174df1a336d2eecf3422 100644
--- a/Gopkg.toml
+++ b/Gopkg.toml
@@ -54,7 +54,7 @@
 
 [[constraint]]
   name = "github.com/ksonnet/ksonnet-lib"
-  revision = "e8077023fa84395ad7976fa750136b39d0aa5f08"
+  revision = "b13dc1c505011ee838ae45324994dac432233000"
 
 [[constraint]]
   name = "github.com/mattn/go-isatty"
diff --git a/generator/ksonnet.go b/generator/ksonnet.go
index d19e347738911b027f0f7533ad0936360006fc5a..647fa7811d8b9bb65796fa08bd4178a43b87aaf1 100644
--- a/generator/ksonnet.go
+++ b/generator/ksonnet.go
@@ -1,17 +1,16 @@
 package generator
 
 import (
-	"encoding/json"
+	"io/ioutil"
+	"os"
 
+	"github.com/davecgh/go-spew/spew"
 	"github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet"
-	"github.com/ksonnet/ksonnet-lib/ksonnet-gen/kubespec"
-	"github.com/ksonnet/ksonnet-lib/ksonnet-gen/kubeversion"
-	log "github.com/sirupsen/logrus"
 )
 
 var (
 	// ksonnetEmitter is the function which emits the ksonnet standard library.
-	ksonnetEmitter = ksonnet.Emit
+	ksonnetEmitter = ksonnet.GenerateLib
 )
 
 // KsonnetLib is the ksonnet standard library for a version of swagger.
@@ -29,33 +28,34 @@ type KsonnetLib struct {
 // Ksonnet generates the ksonnet standard library or returns an error if there was
 // a problem.
 func Ksonnet(swaggerData []byte) (*KsonnetLib, error) {
-	// Deserialize the API object.
-	s := kubespec.APISpec{}
-	if err := json.Unmarshal(swaggerData, &s); err != nil {
+	f, err := ioutil.TempFile("", "")
+	if err != nil {
 		return nil, err
 	}
 
-	s.Text = swaggerData
+	defer os.Remove(f.Name())
 
-	// Emit Jsonnet code.
-	extensionsLibData, k8sLibData, err := ksonnetEmitter(&s, nil, nil)
+	_, err = f.Write(swaggerData)
 	if err != nil {
 		return nil, err
 	}
 
-	// Warn where the Kubernetes version is currently only supported as Beta.
-	if kubeversion.Beta(s.Info.Version) {
-		log.Warnf(`!
-============================================================================================
-Kubernetes version %s is currently supported as Beta; you may encounter unexpected behavior
-============================================================================================`, s.Info.Version)
+	if err = f.Close(); err != nil {
+		return nil, err
+	}
+
+	spew.Dump("---", f.Name(), ksonnetEmitter)
+
+	lib, err := ksonnetEmitter(f.Name())
+	if err != nil {
+		return nil, err
 	}
 
 	kl := &KsonnetLib{
-		K:       extensionsLibData,
-		K8s:     k8sLibData,
+		K:       lib.Extensions,
+		K8s:     lib.K8s,
 		Swagger: swaggerData,
-		Version: s.Info.Version,
+		Version: lib.Version,
 	}
 
 	return kl, nil
diff --git a/generator/ksonnet_test.go b/generator/ksonnet_test.go
index 165dc12dc1e3792f7053bfb82c0c05be879ac1a3..91b0dd80f3904f93a94a8b917f429a71fef101fe 100644
--- a/generator/ksonnet_test.go
+++ b/generator/ksonnet_test.go
@@ -4,7 +4,7 @@ import (
 	"errors"
 	"testing"
 
-	"github.com/ksonnet/ksonnet-lib/ksonnet-gen/kubespec"
+	"github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet"
 )
 
 func TestKsonnet(t *testing.T) {
@@ -16,19 +16,22 @@ func TestKsonnet(t *testing.T) {
 	var (
 		ext            = []byte("k")
 		lib            = []byte("k8s")
-		successfulEmit = func(*kubespec.APISpec, *string, *string) ([]byte, []byte, error) {
-			return ext, lib, nil
+		successfulEmit = func(string) (*ksonnet.Lib, error) {
+			return &ksonnet.Lib{
+				Version:    "v1.7.0",
+				K8s:        lib,
+				Extensions: ext,
+			}, nil
 		}
-		failureEmit = func(*kubespec.APISpec, *string, *string) ([]byte, []byte, error) {
-			return nil, nil, errors.New("failure")
+		failureEmit = func(string) (*ksonnet.Lib, error) {
+			return nil, errors.New("failure")
 		}
 		v170swagger = []byte(`{"info":{"version":"v1.7.0"}}`)
-		v180swagger = []byte(`{"info":{"version":"v1.8.0"}}`)
 	)
 
 	cases := []struct {
 		name        string
-		emitter     func(*kubespec.APISpec, *string, *string) ([]byte, []byte, error)
+		emitter     func(string) (*ksonnet.Lib, error)
 		swaggerData []byte
 		version     string
 		isErr       bool
@@ -41,6 +44,7 @@ func TestKsonnet(t *testing.T) {
 		},
 		{
 			name:        "invalid swagger",
+			emitter:     failureEmit,
 			swaggerData: []byte(`{`),
 			isErr:       true,
 		},
@@ -50,16 +54,12 @@ func TestKsonnet(t *testing.T) {
 			swaggerData: v170swagger,
 			isErr:       true,
 		},
-		{
-			name:        "valid beta swagger",
-			emitter:     successfulEmit,
-			swaggerData: v180swagger,
-			version:     "v1.8.0",
-		},
 	}
 
 	for _, tc := range cases {
 		t.Run(tc.name, func(t *testing.T) {
+			ogKSEmitter := ksonnetEmitter
+			defer func() { ksonnetEmitter = ogKSEmitter }()
 			ksonnetEmitter = tc.emitter
 
 			kl, err := Ksonnet(tc.swaggerData)
@@ -70,7 +70,7 @@ func TestKsonnet(t *testing.T) {
 				}
 			} else {
 				if err != nil {
-					t.Fatal("Ksonnet() returned unexpected error")
+					t.Fatalf("Ksonnet() returned unexpected error: %#v", err)
 				}
 
 				if got, expected := string(kl.K), string(ext); got != expected {
diff --git a/metadata/lib/lib_test.go b/metadata/lib/lib_test.go
index b0792e9de22c7da5b65875164f3f9fb6b040202a..ac5777c72ca92a9c54c90fe4729be3cb60664c13 100644
--- a/metadata/lib/lib_test.go
+++ b/metadata/lib/lib_test.go
@@ -16,6 +16,7 @@ package lib
 
 import (
 	"fmt"
+	"io/ioutil"
 	"os"
 	"testing"
 
@@ -37,14 +38,6 @@ const (
   "definitions": {
   }
 }`
-	blankK8sLib = `// AUTOGENERATED from the Kubernetes OpenAPI specification. DO NOT MODIFY.
-// Kubernetes version: v1.7.0
-
-{
-  local hidden = {
-  },
-}
-`
 )
 
 var testFS = afero.NewMemMapFs()
@@ -74,7 +67,9 @@ func TestGenerateLibData(t *testing.T) {
 	bytes, err := afero.ReadFile(testFS, string(schemaPath))
 	if err != nil {
 		t.Fatalf("Failed to read swagger file at '%s':\n%v", schemaPath, err)
-	} else if actualSwagger := string(bytes); actualSwagger != blankSwaggerData {
+	}
+
+	if actualSwagger := string(bytes); actualSwagger != blankSwaggerData {
 		t.Fatalf("Expected swagger file at '%s' to have value: '%s', got: '%s'", schemaPath, blankSwaggerData, actualSwagger)
 	}
 
@@ -82,7 +77,14 @@ func TestGenerateLibData(t *testing.T) {
 	k8sLibBytes, err := afero.ReadFile(testFS, string(k8sLibPath))
 	if err != nil {
 		t.Fatalf("Failed to read ksonnet-lib file at '%s':\n%v", k8sLibPath, err)
-	} else if actualK8sLib := string(k8sLibBytes); actualK8sLib != blankK8sLib {
+	}
+
+	blankK8sLib, err := ioutil.ReadFile("testdata/k8s.libsonnet")
+	if err != nil {
+		t.Fatalf("Failed to read testdata/k8s.libsonnet: %#v", err)
+	}
+
+	if actualK8sLib := string(k8sLibBytes); actualK8sLib != string(blankK8sLib) {
 		t.Fatalf("Expected swagger file at '%s' to have value: '%s', got: '%s'", k8sLibPath, blankK8sLib, actualK8sLib)
 	}
 }
diff --git a/metadata/lib/testdata/k8s.libsonnet b/metadata/lib/testdata/k8s.libsonnet
new file mode 100644
index 0000000000000000000000000000000000000000..6d4307b8ddfd27956ccc7ca36287d5116e11e542
--- /dev/null
+++ b/metadata/lib/testdata/k8s.libsonnet
@@ -0,0 +1,8 @@
+{
+  "__ksonnet": {
+    checksum: "f942f2f2b70d842504ffa21b502ad044be92110af159342a352bf1ed4c6221e2",
+    kubernetesVersion: "1.7.0",
+  },
+  local hidden = {
+  },
+}
\ No newline at end of file
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/astext/astext.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/astext/astext.go
new file mode 100644
index 0000000000000000000000000000000000000000..c658c81051a0c4b1a252e0a1b89f961d53ad06ed
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/astext/astext.go
@@ -0,0 +1,28 @@
+package astext
+
+import "github.com/google/go-jsonnet/ast"
+
+// ObjectFields is a slice of ObjectField.
+type ObjectFields []ObjectField
+
+// ObjectField wraps ast.ObjectField and adds commenting and the ability to
+// be printed on one line.
+type ObjectField struct {
+	ast.ObjectField
+
+	// Comment is a comment for the object field.
+	Comment *Comment
+
+	// Oneline prints this field on a single line.
+	Oneline bool
+}
+
+// Object wraps ast.Object and adds the ability to be printed on one line.
+type Object struct {
+	ast.Object
+
+	Fields []ObjectField
+
+	// Oneline prints this field on a single line.
+	Oneline bool
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/astext/extensions.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/astext/extensions.go
new file mode 100644
index 0000000000000000000000000000000000000000..0b5eb436943932cd4881ae523b791e9dca1a3ea6
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/astext/extensions.go
@@ -0,0 +1,8 @@
+package astext
+
+// extensions for ast that could live upstream
+
+// Comment is a comment.
+type Comment struct {
+	Text string // represents a single line comment
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/api_object.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/api_object.go
new file mode 100644
index 0000000000000000000000000000000000000000..6e1c77339620cfb9f2802a2bdb5dd23039e33013
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/api_object.go
@@ -0,0 +1,99 @@
+package ksonnet
+
+import (
+	nm "github.com/ksonnet/ksonnet-lib/ksonnet-gen/nodemaker"
+	"github.com/pkg/errors"
+)
+
+// APIObject is an API object.
+type APIObject struct {
+	resource       Object
+	renderFieldsFn renderFieldsFn
+}
+
+// NewAPIObject creates an instance of APIObject.
+func NewAPIObject(resource Object) *APIObject {
+	ao := &APIObject{
+		resource:       resource,
+		renderFieldsFn: renderFields,
+	}
+
+	return ao
+}
+
+// Kind is the kind of api object this is.
+func (a *APIObject) Kind() string {
+	return FormatKind(a.resource.Kind())
+}
+
+// Description is the description of this API object.
+func (a *APIObject) Description() string {
+	return a.resource.Description()
+}
+
+// Node returns an AST node for this api object.
+func (a *APIObject) Node(catalog *Catalog) (*nm.Object, error) {
+	return apiObjectNode(catalog, a)
+}
+
+func (a *APIObject) initNode(catalog *Catalog) (*nm.Object, error) {
+	o := nm.NewObject()
+
+	if a.resource.IsType() {
+		kindObject := nm.OnelineObject()
+		kind := a.resource.Kind()
+		kindObject.Set(nm.InheritedKey("kind"), nm.NewStringDouble(kind))
+		o.Set(nm.LocalKey("kind"), kindObject)
+
+		ctorBase := []nm.Noder{
+			nm.NewVar("apiVersion"),
+			nm.NewVar("kind"),
+		}
+
+		a.setConstructors(o, ctorBase, objectConstructor())
+	} else {
+		a.setConstructors(o, nil, nm.OnelineObject())
+	}
+
+	return o, nil
+}
+
+func (a *APIObject) setConstructors(parent *nm.Object, ctorBase []nm.Noder, defaultCtorBody nm.Noder) error {
+	desc := makeDescriptor(a.resource.Codebase(), a.resource.Group(), a.resource.Kind())
+	ctors := locateConstructors(desc)
+
+	if len(ctors) > 0 {
+		for _, ctor := range ctors {
+			key, err := ctor.Key()
+			if err != nil {
+				return errors.Wrap(err, "generate constructor key")
+			}
+
+			parent.Set(key, ctor.Body(ctorBase...))
+		}
+		return nil
+	}
+
+	parent.Set(nm.FunctionKey("new", []string{}), defaultCtorBody)
+	return nil
+
+}
+
+func objectConstructor() *nm.Binary {
+	return nm.NewBinary(nm.NewVar("apiVersion"), nm.NewVar("kind"), nm.BopPlus)
+}
+
+func apiObjectNode(catalog *Catalog, a *APIObject) (*nm.Object, error) {
+	if catalog == nil {
+		return nil, errors.New("catalog is nil")
+	}
+
+	o, err := a.initNode(catalog)
+	if err != nil {
+		return nil, err
+	}
+	if err := a.renderFieldsFn(catalog, o, "", a.resource.Properties()); err != nil {
+		return nil, err
+	}
+	return o, nil
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/catalog.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/catalog.go
new file mode 100644
index 0000000000000000000000000000000000000000..29aa34d13c809deb587cc93a81333e0bfe5ccdc3
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/catalog.go
@@ -0,0 +1,336 @@
+package ksonnet
+
+import (
+	"strings"
+
+	"github.com/blang/semver"
+	"github.com/go-openapi/spec"
+	"github.com/pkg/errors"
+)
+
+var (
+	blockedReferences = []string{
+		"io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta",
+		"io.k8s.apimachinery.pkg.apis.meta.v1.Status",
+	}
+
+	blockedPropertyNames = []string{
+		"status",
+		"apiVersion",
+		"kind",
+	}
+)
+
+// ExtractFn is a function which extracts properties from a schema.
+type ExtractFn func(*Catalog, map[string]spec.Schema, []string) (map[string]Property, error)
+
+// CatalogOpt is an option for configuring Catalog.
+type CatalogOpt func(*Catalog)
+
+// CatalogOptExtractProperties is a Catalog option for setting the property
+// extractor.
+func CatalogOptExtractProperties(fn ExtractFn) CatalogOpt {
+	return func(c *Catalog) {
+		c.extractFn = fn
+	}
+}
+
+// CatalogOptChecksum is a Catalog option for setting the checksum of the swagger schema.
+func CatalogOptChecksum(checksum string) CatalogOpt {
+	return func(c *Catalog) {
+		c.checksum = checksum
+	}
+}
+
+// Catalog is a catalog definitions
+type Catalog struct {
+	apiSpec    *spec.Swagger
+	extractFn  ExtractFn
+	apiVersion semver.Version
+	paths      map[string]Component
+	checksum   string
+
+	// memos
+	typesCache  []Type
+	fieldsCache []Field
+}
+
+// NewCatalog creates an instance of Catalog.
+func NewCatalog(apiSpec *spec.Swagger, opts ...CatalogOpt) (*Catalog, error) {
+	if apiSpec == nil {
+		return nil, errors.New("apiSpec is nil")
+	}
+
+	if apiSpec.Info == nil {
+		return nil, errors.New("apiSpec Info is nil")
+	}
+
+	parts := strings.SplitN(apiSpec.Info.Version, ".", 3)
+	parts[0] = strings.TrimPrefix(parts[0], "v")
+	vers := strings.Join(parts, ".")
+	apiVersion, err := semver.Parse(vers)
+	if err != nil {
+		return nil, errors.Wrap(err, "invalid apiSpec version")
+	}
+
+	paths, err := parsePaths(apiSpec)
+	if err != nil {
+		return nil, errors.Wrap(err, "parse apiSpec paths")
+	}
+
+	c := &Catalog{
+		apiSpec:    apiSpec,
+		extractFn:  extractProperties,
+		apiVersion: apiVersion,
+		paths:      paths,
+	}
+
+	for _, opt := range opts {
+		opt(c)
+	}
+
+	return c, nil
+}
+
+// Checksum returns the checksum of the swagger schema.
+func (c *Catalog) Checksum() string {
+	return c.checksum
+}
+
+// Version returns the Kubernetes API version represented by this Catalog.
+func (c *Catalog) Version() string {
+	return c.apiVersion.String()
+}
+
+// Types returns a slice of all types.
+func (c *Catalog) Types() ([]Type, error) {
+	if c.typesCache != nil {
+		return c.typesCache, nil
+	}
+
+	var resources []Type
+
+	for name, schema := range c.definitions() {
+		desc, err := ParseDescription(name)
+		if err != nil {
+			return nil, errors.Wrapf(err, "parse description for %s", name)
+		}
+
+		// If there is a path, we can update it as a first class object
+		// in the API. This makes this schema a type.
+		component, ok := c.paths[name]
+		if !ok {
+			continue
+		}
+
+		props, err := c.extractFn(c, schema.Properties, schema.Required)
+		if err != nil {
+			return nil, errors.Wrapf(err, "extract propererties from %s", name)
+		}
+
+		kind := NewType(name, schema.Description, desc.Codebase, desc.Group, component, props)
+
+		resources = append(resources, kind)
+	}
+
+	c.typesCache = resources
+
+	return resources, nil
+}
+
+// Fields returns a slice of all fields.
+func (c *Catalog) Fields() ([]Field, error) {
+	if c.fieldsCache != nil {
+		return c.fieldsCache, nil
+	}
+
+	var types []Field
+
+	for name, schema := range c.definitions() {
+		desc, err := ParseDescription(name)
+		if err != nil {
+			return nil, errors.Wrapf(err, "parse description for %s", name)
+		}
+
+		// If there is a path, this should ot be a hidden object. This
+		// makes this schema a field.
+		if _, ok := c.paths[name]; ok {
+			continue
+		}
+
+		props, err := c.extractFn(c, schema.Properties, schema.Required)
+		if err != nil {
+			return nil, errors.Wrapf(err, "extract propererties from %s", name)
+		}
+		t := NewField(name, schema.Description, desc.Codebase, desc.Group, desc.Version, desc.Kind, props)
+		types = append(types, *t)
+	}
+
+	c.fieldsCache = types
+	return types, nil
+}
+
+func (c *Catalog) isFormatRef(name string) (bool, error) {
+	schema, ok := c.apiSpec.Definitions[name]
+	if !ok {
+		return false, errors.Errorf("%s was not found", name)
+	}
+
+	if schema.Format != "" {
+		return true, nil
+	}
+
+	return false, nil
+}
+
+// Field returns a field by definition id. If the type cannot be found, it returns an error.
+func (c *Catalog) Field(name string) (*Field, error) {
+	types, err := c.Fields()
+	if err != nil {
+		return nil, err
+	}
+
+	for _, ty := range types {
+		if ty.Identifier() == name {
+			return &ty, nil
+		}
+	}
+
+	return nil, errors.Errorf("%s was not found", name)
+}
+
+// Resource returns a resource by group, version, kind. If the field cannot be found,
+// it returns an error
+func (c *Catalog) Resource(group, version, kind string) (*Type, error) {
+	resources, err := c.Types()
+	if err != nil {
+		return nil, err
+	}
+
+	for _, resource := range resources {
+		if group == resource.Group() &&
+			version == resource.Version() &&
+			kind == resource.Kind() {
+			return &resource, nil
+		}
+	}
+
+	return nil, errors.Errorf("unable to find %s.%s.%s",
+		group, version, kind)
+}
+
+// TypeByID returns a type by identifier.
+func (c *Catalog) TypeByID(id string) (*Type, error) {
+	resources, err := c.Types()
+	if err != nil {
+		return nil, err
+	}
+
+	for _, resource := range resources {
+		if resource.Identifier() == id {
+			return &resource, nil
+		}
+	}
+
+	return nil, errors.Errorf("unable to find type %q", id)
+}
+
+// TypesWithDescendant returns types who have the specified definition as a descendant.
+// This list does not include List types (e.g. DeploymentList).
+func (c *Catalog) TypesWithDescendant(definition string) ([]Type, error) {
+	types, err := c.Types()
+	if err != nil {
+		return nil, errors.Wrap(err, "retrieve types")
+	}
+
+	var out []Type
+	for _, ty := range types {
+
+		if strings.HasSuffix(ty.Kind(), "List") {
+			continue
+		}
+		tf, err := c.descend(definition, ty.Properties())
+		if err != nil {
+			return nil, err
+		}
+
+		if tf {
+			out = append(out, ty)
+		}
+	}
+
+	return out, nil
+}
+
+func (c *Catalog) find(id string) (Object, error) {
+	f, err := c.Field(id)
+	if err == nil {
+		return f, nil
+	}
+
+	t, err := c.TypeByID(id)
+	if err != nil {
+		return nil, errors.Errorf("unable to find object %q", id)
+	}
+
+	return t, nil
+}
+
+func (c *Catalog) descend(definition string, m map[string]Property) (bool, error) {
+
+	for _, prop := range m {
+		if ref := prop.Ref(); ref != "" {
+
+			if ref == definition {
+				return true, nil
+			}
+
+			// NOTE: if this is a reference to json schema, bail out because this is recursive.
+			if ref == "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps" {
+				continue
+			}
+
+			f, err := c.find(ref)
+			if err != nil {
+				return false, errors.Wrapf(err, "find field %s", ref)
+			}
+
+			tf, err := c.descend(definition, f.Properties())
+			if err != nil {
+				return false, err
+			}
+
+			if tf {
+				return true, nil
+			}
+		}
+	}
+
+	return false, nil
+}
+
+func isValidDefinition(name string, ver semver.Version) bool {
+	checkVer := semver.Version{Major: 1, Minor: 7}
+	if ver.GTE(checkVer) {
+		return !strings.HasPrefix(name, "io.k8s.kubernetes.pkg.api")
+	}
+
+	return true
+}
+
+// extractRef extracts a ref from a schema.
+func extractRef(schema spec.Schema) string {
+	return strings.TrimPrefix(schema.Ref.String(), "#/definitions/")
+}
+
+func (c *Catalog) definitions() spec.Definitions {
+	out := spec.Definitions{}
+
+	for name, schema := range c.apiSpec.Definitions {
+		if isValidDefinition(name, c.apiVersion) {
+			out[name] = schema
+		}
+	}
+
+	return out
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/component.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/component.go
new file mode 100644
index 0000000000000000000000000000000000000000..123aa18acc60ba0afdec32ecf44c05688a3347dd
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/component.go
@@ -0,0 +1,77 @@
+package ksonnet
+
+import (
+	"fmt"
+
+	"github.com/go-openapi/spec"
+	"github.com/pkg/errors"
+)
+
+const (
+	extensionGroupVersionKind = "x-kubernetes-group-version-kind"
+)
+
+// Component is resource information provided in the k8s swagger schema
+// which contains the group, kind, and version for a definition.
+type Component struct {
+	Group   string
+	Kind    string
+	Version string
+}
+
+// NewComponent extracts component information from a schema.
+func NewComponent(s spec.Schema) (*Component, error) {
+	re := componentExtractor{schema: s}
+	group := re.extract("group")
+	kind := re.extract("kind")
+	version := re.extract("version")
+
+	if re.err != nil {
+		return nil, re.err
+	}
+
+	return &Component{
+		Group:   group,
+		Kind:    kind,
+		Version: version,
+	}, nil
+}
+
+func (c *Component) String() string {
+	group := c.Group
+	if group == "" {
+		group = "core"
+	}
+
+	return fmt.Sprintf("%s.%s.%s", group, c.Version, c.Kind)
+}
+
+type componentExtractor struct {
+	err    error
+	schema spec.Schema
+}
+
+func (re *componentExtractor) extract(key string) string {
+	if re.err != nil {
+		return ""
+	}
+
+	i, ok := re.schema.Extensions[extensionGroupVersionKind]
+	if !ok {
+		re.err = errors.New("no group/kind/version extension")
+		return ""
+	}
+
+	s, ok := i.([]interface{})
+	if ok {
+		m, ok := s[0].(map[string]interface{})
+		if ok {
+			str, ok := m[key].(string)
+			if ok {
+				return str
+			}
+		}
+	}
+
+	return ""
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/constructors.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/constructors.go
new file mode 100644
index 0000000000000000000000000000000000000000..afd433d8c99efaa667c07cc0321f42065aedd661
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/constructors.go
@@ -0,0 +1,187 @@
+package ksonnet
+
+import (
+	"regexp"
+	"sort"
+
+	"github.com/google/go-jsonnet/ast"
+	nm "github.com/ksonnet/ksonnet-lib/ksonnet-gen/nodemaker"
+	"github.com/pkg/errors"
+)
+
+var (
+	// reCtorSetter is a regex that matches function names. It'll successfully
+	// match `withName`, `foo.withName`, and `foo.bar.withName`.
+	reCtorSetter = regexp.MustCompile(`((^.*?)\.)*(with\w+)$`)
+)
+
+func matchCtorSetter(in string) (string, string, error) {
+	match := reCtorSetter.FindAllStringSubmatch(in, -1)
+	if len(match) == 0 {
+		return "", "", errors.New("no match")
+	}
+
+	cur := match[0]
+	if cur[1] == "" {
+		return "self", cur[3], nil
+	}
+
+	return "self." + cur[2], cur[3], nil
+}
+
+type constructor struct {
+	name   string
+	params []constructorParam
+}
+
+func newConstructor(name string, params ...constructorParam) *constructor {
+	return &constructor{
+		name:   name,
+		params: params,
+	}
+}
+
+// Key creates an object key for the constructor.
+func (c *constructor) Key() (nm.Key, error) {
+	var args []nm.OptionalArg
+
+	for _, param := range c.params {
+		option, err := param.Option()
+		if err != nil {
+			return nm.Key{}, errors.Wrap(err, "unable to create key from param")
+		}
+
+		args = append(args, option)
+	}
+
+	key := nm.FunctionKey(c.name, []string{}, nm.KeyOptNamedParams(args...))
+	return key, nil
+}
+
+func (c *constructor) Body(baseNodes ...nm.Noder) nm.Noder {
+	var items []nm.Noder
+	for _, node := range baseNodes {
+		items = append(items, node)
+	}
+
+	// collection functions so they can be de-duplicated.
+	funs := make(map[string][]argRef)
+	for _, param := range c.params {
+		path, fn, err := matchCtorSetter(param.function)
+		if err != nil {
+			// TODO should we handle this error?
+			continue
+		}
+
+		if _, ok := funs[path]; !ok {
+			funs[path] = make([]argRef, 0)
+		}
+
+		funs[path] = append(funs[path], argRef{name: param.name, fn: fn})
+	}
+
+	var funNames []string
+	for funName := range funs {
+		funNames = append(funNames, funName)
+	}
+	sort.Strings(funNames)
+
+	for _, funName := range funNames {
+
+		call := nm.NewCall(funName)
+
+		var curApply *ctorApply
+		var addedCall bool
+
+		ars := funs[funName]
+		sort.Slice(ars, func(i, j int) bool {
+			return ars[i].fn < ars[j].fn
+		})
+
+		for _, ar := range ars {
+			indexID := ast.Identifier(ar.fn)
+			index := &ast.Index{Id: &indexID}
+			if !addedCall {
+				index.Target = call.Node()
+				addedCall = true
+			} else {
+				index.Target = curApply.Node()
+			}
+
+			arg := &ast.Var{Id: ast.Identifier(ar.name)}
+			apply := ast.Apply{
+				Arguments: ast.Arguments{Positional: ast.Nodes{arg}},
+				Target:    index,
+			}
+
+			curApply = &ctorApply{Apply: apply}
+		}
+
+		items = append(items, curApply)
+	}
+
+	return nm.Combine(items...)
+}
+
+type ctorApply struct {
+	ast.Apply
+}
+
+func (ca *ctorApply) Node() ast.Node {
+	return &ca.Apply
+}
+
+type argRef struct {
+	name string
+	fn   string
+}
+
+type constructorParam struct {
+	name         string
+	function     string
+	defaultValue interface{}
+}
+
+func newConstructorParam(name, function string, defaultValue interface{}) *constructorParam {
+	if defaultValue == nil {
+		defaultValue = ""
+	}
+
+	return &constructorParam{
+		name:         name,
+		function:     function,
+		defaultValue: defaultValue,
+	}
+}
+
+func (cp *constructorParam) Option() (nm.OptionalArg, error) {
+	var node nm.Noder
+
+	var err error
+
+	switch t := cp.defaultValue.(type) {
+	case string:
+		node = nm.NewStringDouble(t)
+	case map[string]interface{}:
+		node, err = nm.KVFromMap(t)
+		if err != nil {
+			return nm.OptionalArg{}, errors.Wrap(err, "invalid parameter")
+		}
+	case []string:
+		var items []nm.Noder
+		for _, item := range t {
+			items = append(items, nm.NewStringDouble(item))
+		}
+		node = nm.NewArray(items)
+	case float64:
+		node = nm.NewFloat(t)
+	case int:
+		node = nm.NewInt(t)
+	case bool:
+		node = nm.NewBoolean(t)
+	default:
+		return nm.OptionalArg{}, errors.Errorf("unable to use type %T in param", t)
+	}
+
+	return nm.OptionalArg{Name: cp.name, Default: node}, nil
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/custom_constructor.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/custom_constructor.go
new file mode 100644
index 0000000000000000000000000000000000000000..0cd398b7f8f678b732c97813de7a443aa21a80b0
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/custom_constructor.go
@@ -0,0 +1,214 @@
+package ksonnet
+
+// NOTE: custom constructors will be removed at ksonnet 0.11
+
+func locateConstructors(desc Description) []constructor {
+	ctors, ok := customConstructors[desc]
+	if !ok {
+		return nil
+	}
+
+	return ctors
+}
+
+func makeDescriptor(codebase, group, kind string) Description {
+	return Description{
+		Codebase: codebase,
+		Group:    group,
+		Kind:     kind,
+	}
+}
+
+var (
+	customConstructors = map[Description][]constructor{
+		makeDescriptor("api", "apps", "Deployment"):         deploymentCtor,
+		makeDescriptor("api", "apps", "DeploymentList"):     objectList,
+		makeDescriptor("api", "apps", "DeploymentRollback"): deploymentRollbackCtor,
+		makeDescriptor("api", "apps", "Scale"):              scaleCtor,
+		makeDescriptor("api", "apps", "StatefulSet"):        statefulSetCtor,
+		makeDescriptor("api", "apps", "StatefulSetList"):    objectList,
+
+		makeDescriptor("api", "extensions", "Deployment"):         deploymentCtor,
+		makeDescriptor("api", "extensions", "DeploymentList"):     objectList,
+		makeDescriptor("api", "extensions", "DeploymentRollback"): deploymentRollbackCtor,
+		makeDescriptor("api", "extensions", "Scale"):              scaleCtor,
+		makeDescriptor("api", "extensions", "StatefulSet"):        statefulSetCtor,
+		makeDescriptor("api", "extensions", "StatefulSetList"):    objectList,
+
+		makeDescriptor("api", "authentication", "TokenReview"): []constructor{
+			*newConstructor(
+				"new",
+				*newConstructorParam("token", "mixin.spec.withToken", nil),
+			),
+		},
+
+		makeDescriptor("api", "autoscaling", "HorizontalPodAutoscalerList"): objectList,
+		makeDescriptor("api", "autoscaling", "Scale"):                       scaleCtor,
+
+		makeDescriptor("api", "batch", "JobList"):     objectList,
+		makeDescriptor("api", "batch", "CronJobList"): objectList,
+
+		makeDescriptor("api", "certificates", "CertificateSigningRequestList"): objectList,
+
+		makeDescriptor("api", "core", "ConfigMap"): []constructor{
+			*newConstructor(
+				"new",
+				*newConstructorParam("name", "mixin.metadata.withName", nil),
+			),
+		},
+		makeDescriptor("api", "core", "ConfigMapList"): objectList,
+		makeDescriptor("api", "core", "Container"): []constructor{
+			*newConstructor(
+				"new",
+				*newConstructorParam("name", "withName", nil),
+				*newConstructorParam("image", "withImage", nil),
+			),
+		},
+		makeDescriptor("api", "core", "ContainerPort"): []constructor{
+			*newConstructor("new", *newConstructorParam("containerPort", "withContainerPort", nil)),
+			*newConstructor("newNamed",
+				*newConstructorParam("containerPort", "withContainerPort", nil),
+				*newConstructorParam("name", "withName", nil),
+			),
+		},
+		makeDescriptor("api", "core", "EndpointsList"): objectList,
+		makeDescriptor("api", "core", "EnvVar"): []constructor{
+			*newConstructor("new",
+				*newConstructorParam("name", "withName", nil),
+				*newConstructorParam("value", "withValue", nil)),
+			*newConstructor("fromSecretRef",
+				*newConstructorParam("name", "withName", nil),
+				*newConstructorParam("secretRefName", "mixin.valueFrom.secretKeyRef.withName", nil),
+				*newConstructorParam("secretRefKey", "mixin.valueFrom.secretKeyRef.withKey", nil)),
+			*newConstructor("fromFieldPath",
+				*newConstructorParam("name", "withName", nil),
+				*newConstructorParam("fieldPath", "mixin.valueFrom.fieldRef.withFieldPath", nil)),
+		},
+		makeDescriptor("api", "core", "EventList"): objectList,
+		makeDescriptor("api", "core", "KeyToPath"): []constructor{
+			*newConstructor("new",
+				*newConstructorParam("key", "withKey", nil),
+				*newConstructorParam("path", "withPath", nil)),
+		},
+		makeDescriptor("api", "core", "LimitRangeList"): objectList,
+		makeDescriptor("api", "core", "Namespace"): []constructor{
+			*newConstructor("new",
+				*newConstructorParam("name", "withName", nil)),
+		},
+		makeDescriptor("api", "core", "NamespaceList"):             objectList,
+		makeDescriptor("api", "core", "NodeList"):                  objectList,
+		makeDescriptor("api", "core", "PersistentVolumeClaimList"): objectList,
+		makeDescriptor("api", "core", "PersistentVolumeList"):      objectList,
+		makeDescriptor("api", "core", "PodList"):                   objectList,
+		makeDescriptor("api", "core", "PodTemplateList"):           objectList,
+		makeDescriptor("api", "core", "ReplicationControllerList"): objectList,
+		makeDescriptor("api", "core", "ResourceQuotaList"):         objectList,
+		makeDescriptor("api", "core", "Secret"): []constructor{
+			*newConstructor("new",
+				*newConstructorParam("name", "mixin.metadata.withName", nil),
+				*newConstructorParam("data", "withData", nil),
+				*newConstructorParam("type", "withType", "Opaque")),
+			*newConstructor("new",
+				*newConstructorParam("name", "mixin.metadata.withName", nil),
+				*newConstructorParam("stringDate", "withStringData", nil),
+				*newConstructorParam("type", "withType", "Opaque")),
+		},
+		makeDescriptor("api", "core", "SecretList"): objectList,
+		makeDescriptor("api", "core", "Service"): []constructor{
+			*newConstructor("new",
+				*newConstructorParam("name", "mixin.metadata.withName", nil),
+				*newConstructorParam("selector", "mixin.spec.withSelector", nil),
+				*newConstructorParam("ports", "mixin.spec.withPorts", nil)),
+		},
+		makeDescriptor("api", "core", "ServiceAccount"): []constructor{
+			*newConstructor("new",
+				*newConstructorParam("name", "mixin.metadata.withName", nil)),
+		},
+		makeDescriptor("api", "core", "ServiceAccountList"): objectList,
+		makeDescriptor("api", "core", "ServiceList"):        objectList,
+		makeDescriptor("api", "core", "ServicePort"): []constructor{
+			*newConstructor("new",
+				*newConstructorParam("port", "withPort", nil),
+				*newConstructorParam("targetPort", "withTargetPort", nil)),
+			*newConstructor("newNamed",
+				*newConstructorParam("name", "mixin.metadata.withName", nil),
+				*newConstructorParam("port", "withPort", nil),
+				*newConstructorParam("targetPort", "withTargetPort", nil)),
+		},
+		makeDescriptor("api", "core", "Volume"): []constructor{
+			*newConstructor(
+				"fromConfigMap",
+				*newConstructorParam("name", "withName", nil),
+				*newConstructorParam("configMapName", "mixin.configMap.withName", nil),
+				*newConstructorParam("configMapItems", "mixin.configMap.withItems", nil)),
+			*newConstructor("fromEmptyDir",
+				*newConstructorParam("name", "withName", nil),
+				*newConstructorParam("emptyDir", "mixin.emptyDir.mixinInstance",
+					map[string]interface{}{})),
+			*newConstructor("fromPersistentVolumeClaim",
+				*newConstructorParam("name", "withName", nil),
+				*newConstructorParam("emptyDir", "mixin.persistentVolumeClaim.withClaimName", nil)),
+			*newConstructor("fromHostPath",
+				*newConstructorParam("name", "withName", nil),
+				*newConstructorParam("hostPath", "mixin.hostPath.withPath", nil)),
+			*newConstructor("fromSecret",
+				*newConstructorParam("name", "withName", nil),
+				*newConstructorParam("secretName", "mixin.secret.withSecretName", nil)),
+		},
+		makeDescriptor("api", "core", "VolumeMount"): []constructor{
+			*newConstructor("new",
+				*newConstructorParam("name", "withName", nil),
+				*newConstructorParam("mountPath", "withMountPath", nil),
+				*newConstructorParam("readOnly", "withReadOnly", false)),
+		},
+	}
+
+	// customConstructor definitions
+
+	deploymentCtor = []constructor{
+		*newConstructor(
+			"new",
+			*newConstructorParam("name", "mixin.metadata.withName", nil),
+			*newConstructorParam("replicas", "mixin.spec.withReplicas", 1),
+			*newConstructorParam("containers", "mixin.spec.template.spec.withContainers", nil),
+			*newConstructorParam("podLabels", "mixin.spec.template.metadata.withLabels",
+				map[string]interface{}{"app": "name"}),
+		),
+	}
+	deploymentRollbackCtor = []constructor{
+		*newConstructor(
+			"new",
+			*newConstructorParam("name", "withName", nil),
+		),
+	}
+	objectList = []constructor{
+		*newConstructor(
+			"new",
+			*newConstructorParam("items", "withItems", nil),
+		),
+	}
+	scaleCtor = []constructor{
+		*newConstructor(
+			"new",
+			*newConstructorParam("replicas", "mixin.spec.withReplicas", 1),
+		),
+	}
+	statefulSetCtor = []constructor{
+		*newConstructor(
+			"new",
+			*newConstructorParam("name", "mixin.metadata.withName", nil),
+			*newConstructorParam("replicas", "mixin.spec.withReplicas", 1),
+			*newConstructorParam("containers", "mixin.spec.template.spec.withContainers", nil),
+			*newConstructorParam("volumeClaims", "mixin.spec.withVolumeClaimTemplates", nil),
+			*newConstructorParam("podLabels", "mixin.spec.template.metadata.withLabels", map[string]interface{}{
+				"app": "name",
+			}),
+		),
+	}
+	tokenReviewCtor = []constructor{
+		*newConstructor(
+			"new",
+			*newConstructorParam("token", "mixin.spec.withToken", nil),
+		),
+	}
+)
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/description.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/description.go
new file mode 100644
index 0000000000000000000000000000000000000000..6c3dfda5d062de08d60af728db4d201f7b6714bb
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/description.go
@@ -0,0 +1,97 @@
+package ksonnet
+
+import (
+	"fmt"
+	"regexp"
+)
+
+const (
+	groupCore = "core"
+)
+
+var (
+	reNames = []*regexp.Regexp{
+		// Core API, pre-1.8 Kubernetes OR non-Kubernetes codebase APIs
+		regexp.MustCompile(`io\.k8s\.(?P<codebase>\S+)\.pkg\.api\.(?P<version>\S+)\.(?P<kind>\S+)`),
+		// Core API, 1.8+ Kubernetes
+		regexp.MustCompile(`io\.k8s\.api\.(?P<packageType>core)\.(?P<version>\S+)\.(?P<kind>\S+)`),
+		// Other APIs, pre-1.8 Kubernetes OR non-Kubernetes codebase APIs
+		regexp.MustCompile(`io\.k8s\.(?P<codebase>\S+)\.pkg\.(?P<packageType>apis)\.(?P<group>\S+)\.(?P<version>\S+)\.(?P<kind>\S+)`),
+		// Other APIs, 1.8+ Kubernetes
+		regexp.MustCompile(`io\.k8s\.api\.(?P<group>\S+)\.(?P<version>\S+)\.(?P<kind>\S+)`),
+		// Util packageType
+		regexp.MustCompile(`io\.k8s\.(?P<codebase>\S+)\.pkg\.(?P<packageType>util)\.(?P<version>\S+)\.(?P<kind>\S+)`),
+		// Version packageType
+		regexp.MustCompile(`io\.k8s\.(?P<codebase>\S+)\.pkg\.(?P<packageType>version)\.(?P<kind>\S+)`),
+		// Runtime packageType
+		regexp.MustCompile(`io\.k8s\.(?P<codebase>\S+)\.pkg\.(?P<packageType>runtime)\.(?P<kind>\S+)`),
+	}
+)
+
+// UnknownDefinitionError is an error signifying an unknown definition.
+type UnknownDefinitionError struct {
+	name string
+}
+
+var _ error = (*UnknownDefinitionError)(nil)
+
+// NewUnknownDefinitionError creates an instance of UnknownDefinitionError.
+func NewUnknownDefinitionError(name string) *UnknownDefinitionError {
+	return &UnknownDefinitionError{
+		name: name,
+	}
+}
+
+func (e *UnknownDefinitionError) Error() string {
+	return fmt.Sprintf("%q is not a known definition name", e.name)
+}
+
+// Description is a description of a Kubernetes definition name.
+type Description struct {
+	Name     string
+	Version  string
+	Kind     string
+	Group    string
+	Codebase string
+}
+
+// Validate validates the Description. A description is valid if it has a version.
+func (d *Description) Validate() error {
+	if d.Version == "" {
+		return fmt.Errorf("version is nil for %q", d.Name)
+	}
+
+	return nil
+}
+
+// ParseDescription takes a definition name and returns a Description.
+func ParseDescription(name string) (*Description, error) {
+	for _, r := range reNames {
+		if match := r.FindStringSubmatch(name); len(match) > 0 {
+
+			result := make(map[string]string)
+			for i, name := range r.SubexpNames() {
+				if i != 0 {
+					result[name] = match[i]
+				}
+			}
+
+			codebase := result["codebase"]
+			if codebase == "" {
+				codebase = "api"
+			}
+
+			d := &Description{
+				Name:     name,
+				Version:  result["version"],
+				Kind:     result["kind"],
+				Group:    result["group"],
+				Codebase: codebase,
+			}
+
+			return d, nil
+		}
+	}
+
+	return nil, &UnknownDefinitionError{name: name}
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/document.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/document.go
new file mode 100644
index 0000000000000000000000000000000000000000..48180853d4f895a0c7fe7a8bf08f3be474b6eaba
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/document.go
@@ -0,0 +1,188 @@
+package ksonnet
+
+import (
+	"sort"
+
+	nm "github.com/ksonnet/ksonnet-lib/ksonnet-gen/nodemaker"
+	"github.com/pkg/errors"
+)
+
+type renderNodeFn func(c *Catalog, a *APIObject) (*nm.Object, error)
+
+// Document represents a ksonnet lib document.
+type Document struct {
+	catalog *Catalog
+
+	// these are defined to aid testing Document
+	typesFn            func() ([]Type, error)
+	fieldsFn           func() ([]Field, error)
+	renderFn           func(fn renderNodeFn, c *Catalog, o *nm.Object, groups []Group) error
+	renderGroups       func(doc *Document, container *nm.Object) error
+	renderHiddenGroups func(doc *Document, container *nm.Object) error
+	objectNodeFn       func(c *Catalog, a *APIObject) (*nm.Object, error)
+}
+
+// NewDocument creates an instance of Document.
+func NewDocument(catalog *Catalog) (*Document, error) {
+	if catalog == nil {
+		return nil, errors.New("catalog is nil")
+	}
+
+	return &Document{
+		catalog:            catalog,
+		typesFn:            catalog.Types,
+		fieldsFn:           catalog.Fields,
+		renderFn:           render,
+		renderGroups:       renderGroups,
+		renderHiddenGroups: renderHiddenGroups,
+		objectNodeFn:       apiObjectNode,
+	}, nil
+}
+
+// Groups returns an alphabetically sorted list of groups.
+func (d *Document) Groups() ([]Group, error) {
+	resources, err := d.typesFn()
+	if err != nil {
+		return nil, errors.Wrap(err, "retrieve resources")
+	}
+
+	var nodeObjects []Object
+	for _, resource := range resources {
+		res := resource
+		nodeObjects = append(nodeObjects, &res)
+	}
+
+	return d.groups(nodeObjects)
+}
+
+// HiddenGroups returns an alphabetically sorted list of hidden groups.
+func (d *Document) HiddenGroups() ([]Group, error) {
+	resources, err := d.fieldsFn()
+	if err != nil {
+		return nil, errors.Wrap(err, "retrieve types")
+	}
+
+	var nodeObjects []Object
+	for _, resource := range resources {
+		res := resource
+		nodeObjects = append(nodeObjects, &res)
+	}
+
+	return d.groups(nodeObjects)
+}
+
+func (d *Document) groups(resources []Object) ([]Group, error) {
+	gMap := make(map[string]*Group)
+
+	for i := range resources {
+		res := resources[i]
+		name := res.Group()
+
+		g, ok := gMap[name]
+		if !ok {
+			g = NewGroup(name)
+			gMap[name] = g
+		}
+
+		g.AddResource(res)
+		gMap[name] = g
+	}
+
+	var groupNames []string
+
+	for name := range gMap {
+		groupNames = append(groupNames, name)
+	}
+
+	sort.Strings(groupNames)
+
+	var groups []Group
+
+	for _, name := range groupNames {
+		g := gMap[name]
+		groups = append(groups, *g)
+	}
+
+	return groups, nil
+}
+
+// Node converts a document to a node.
+func (d *Document) Node() (*nm.Object, error) {
+	out := nm.NewObject()
+
+	metadata := map[string]interface{}{
+		"kubernetesVersion": d.catalog.Version(),
+		"checksum":          d.catalog.Checksum(),
+	}
+	metadataObj, err := nm.KVFromMap(metadata)
+	if err != nil {
+		return nil, errors.Wrap(err, "create metadata key")
+	}
+	out.Set(nm.InheritedKey("__ksonnet"), metadataObj)
+
+	if err := d.renderGroups(d, out); err != nil {
+		return nil, err
+	}
+
+	hidden := nm.NewObject()
+
+	if err := d.renderHiddenGroups(d, hidden); err != nil {
+		return nil, err
+	}
+
+	out.Set(nm.LocalKey("hidden"), hidden)
+
+	return out, nil
+}
+
+func render(fn renderNodeFn, catalog *Catalog, o *nm.Object, groups []Group) error {
+	for _, group := range groups {
+		groupNode := group.Node()
+		for _, version := range group.Versions() {
+			versionNode := version.Node()
+			for _, apiObject := range version.APIObjects() {
+				objectNode, err := fn(catalog, &apiObject)
+				if err != nil {
+					return errors.Wrapf(err, "create node %s", apiObject.Kind())
+				}
+
+				versionNode.Set(
+					nm.NewKey(apiObject.Kind(), nm.KeyOptComment(apiObject.Description())),
+					objectNode)
+			}
+
+			groupNode.Set(nm.NewKey(version.Name()), versionNode)
+		}
+
+		o.Set(nm.NewKey(group.Name()), groupNode)
+	}
+
+	return nil
+
+}
+
+func renderGroups(d *Document, container *nm.Object) error {
+	groups, err := d.Groups()
+	if err != nil {
+		return errors.Wrap(err, "retrieve groups")
+	}
+
+	if err = d.renderFn(d.objectNodeFn, d.catalog, container, groups); err != nil {
+		return errors.Wrap(err, "render groups")
+	}
+
+	return nil
+}
+
+func renderHiddenGroups(d *Document, container *nm.Object) error {
+	groups, err := d.HiddenGroups()
+	if err != nil {
+		return errors.Wrap(err, "retrieve hidden groups")
+	}
+
+	if err = d.renderFn(d.objectNodeFn, d.catalog, container, groups); err != nil {
+		return errors.Wrap(err, "render hidden groups")
+	}
+
+	return nil
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/extension.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/extension.go
new file mode 100644
index 0000000000000000000000000000000000000000..18fef747fdf18f2874fb5c4a46d39af4e46ee447
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/extension.go
@@ -0,0 +1,286 @@
+package ksonnet
+
+import (
+	"fmt"
+	"sort"
+	"strings"
+
+	nm "github.com/ksonnet/ksonnet-lib/ksonnet-gen/nodemaker"
+	"github.com/pkg/errors"
+)
+
+const (
+	localK8s = "k8s"
+)
+
+// Extension represents a ksonnet lib extension document.
+type Extension struct {
+	catalog *Catalog
+}
+
+// NewExtension creates an an instance of Extension.
+func NewExtension(catalog *Catalog) *Extension {
+	return &Extension{
+		catalog: catalog,
+	}
+}
+
+// Node converts an extension to a node.
+func (e *Extension) Node() (nm.Noder, error) {
+	ext, err := e.genK8sExtension()
+	if err != nil {
+		return nil, err
+	}
+
+	extBinary := nm.NewBinary(nm.NewVar(localK8s), ext, nm.BopPlus)
+
+	fns := genMapContainers(extBinary)
+
+	k8sImportFile := nm.NewImport("k8s.libsonnet")
+	k8sImport := nm.NewLocal(localK8s, k8sImportFile, fns)
+
+	return k8sImport, nil
+}
+
+func (e *Extension) genK8sExtension() (*nm.Object, error) {
+	gi := makeGroupItems()
+
+	e.listExtension(gi)
+
+	if err := e.mapContainersExtension(gi); err != nil {
+		return nil, errors.Wrap(err, "map container extensions")
+	}
+
+	return gi.Node(), nil
+}
+
+func (e *Extension) mapContainersExtension(gi *groupItems) error {
+	types, err := e.catalog.TypesWithDescendant("io.k8s.api.core.v1.PodSpec")
+	if err != nil {
+		return errors.Wrap(err, "find types with PodSec")
+	}
+
+	mapping := nm.NewObject()
+	mapping.Set(
+		nm.FunctionKey("mapContainers", []string{"f"}),
+		nm.ApplyCall("fn.mapContainers", nm.NewVar("f")),
+	)
+
+	mapping.Set(
+		nm.FunctionKey("mapContainersWithName", []string{"names", "f"}),
+		nm.ApplyCall("fn.mapContainersWithName", nm.NewVar("names"), nm.NewVar("f")),
+	)
+
+	for _, ty := range types {
+		parts := strings.Split(ty.component.String(), ".")
+		gi.add(parts[0], parts[1], FormatKind(parts[2]), mapping, true)
+	}
+
+	return nil
+}
+
+func (e *Extension) listExtension(gi *groupItems) {
+	apiVersion := nm.NewObject()
+	apiVersion.Set(nm.InheritedKey("apiVersion"), nm.NewStringDouble("v1"))
+
+	kind := nm.NewObject()
+	kind.Set(nm.InheritedKey("kind"), nm.NewStringDouble("List"))
+
+	items := nm.ApplyCall("self.items", nm.NewVar("items"))
+
+	o := nm.NewObject()
+	o.Set(
+		nm.FunctionKey("new", []string{"items"}),
+		nm.Combine(apiVersion, kind, items),
+	)
+	o.Set(
+		nm.FunctionKey("items", []string{"items"}),
+		convertToArray("items", "", true),
+	)
+
+	gi.add("core", "v1", "list", o, false)
+}
+
+type nodeMixin struct {
+	node    nm.Noder
+	isMixin bool
+}
+
+type groupItems struct {
+	groups map[string]map[string]map[string]nodeMixin
+}
+
+func makeGroupItems() *groupItems {
+	return &groupItems{
+		groups: make(map[string]map[string]map[string]nodeMixin),
+	}
+}
+
+func (gi *groupItems) add(group, version, key string, node nm.Noder, isMixin bool) {
+	g, ok := gi.groups[group]
+	if !ok {
+		g = make(map[string]map[string]nodeMixin)
+		gi.groups[group] = g
+	}
+
+	v, ok := g[version]
+	if !ok {
+		v = make(map[string]nodeMixin)
+		g[version] = v
+	}
+
+	v[key] = nodeMixin{node: node, isMixin: isMixin}
+}
+
+func (gi *groupItems) Node() *nm.Object {
+	var groupNames []string
+	for name := range gi.groups {
+		groupNames = append(groupNames, name)
+	}
+	sort.Strings(groupNames)
+
+	o := nm.NewObject()
+
+	for _, groupName := range groupNames {
+		group := gi.groups[groupName]
+		groupObject := nm.NewObject()
+
+		var versionNames []string
+		for name := range group {
+			versionNames = append(versionNames, name)
+		}
+		sort.Strings(versionNames)
+
+		for _, versionName := range versionNames {
+			version := group[versionName]
+			versionObject := nm.NewObject()
+
+			var keyNames []string
+			for name := range version {
+				keyNames = append(keyNames, name)
+			}
+			sort.Strings(keyNames)
+
+			for _, keyName := range keyNames {
+				node := version[keyName].node
+				isMixin := version[keyName].isMixin
+
+				if isMixin {
+					parent := nm.NewCall(fmt.Sprintf("k8s.%s.%s.%s", groupName, versionName, keyName))
+					node = nm.NewBinary(parent, node, nm.BopPlus)
+				}
+				versionObject.Set(nm.NewKey(keyName), node)
+			}
+
+			parent := nm.NewCall(fmt.Sprintf("k8s.%s.%s", groupName, versionName))
+			groupObject.Set(nm.NewKey(versionName), nm.NewBinary(parent, versionObject, nm.BopPlus))
+		}
+
+		parent := nm.NewCall(fmt.Sprintf("k8s.%s", groupName))
+		o.Set(nm.NewKey(groupName), nm.NewBinary(parent, groupObject, nm.BopPlus))
+	}
+
+	return o
+}
+
+func genMapContainers(body nm.Noder) *nm.Local {
+	o := nm.NewObject()
+
+	o.Set(
+		nm.FunctionKey("mapContainers", []string{"f"}),
+		createMapContainersFn(),
+	)
+
+	o.Set(
+		nm.FunctionKey("mapContainersWithName", []string{"names", "f"}),
+		createMapContainersWithName(),
+	)
+
+	return nm.NewLocal("fn", o, body)
+}
+
+func createMapContainersFn() *nm.Object {
+	o := nm.NewObject()
+	o.Set(
+		nm.LocalKey("podContainers"),
+		nm.NewCall("super.spec.template.spec.containers"),
+	)
+
+	templateSpecObject := nm.NewObject()
+	templateSpecObject.Set(
+		nm.InheritedKey("containers"),
+		nm.ApplyCall("std.map", nm.NewVar("f"), nm.NewVar("podContainers")),
+	)
+
+	templateObject := nm.NewObject()
+	templateObject.Set(
+		nm.InheritedKey("spec", nm.KeyOptMixin(true)),
+		templateSpecObject,
+	)
+
+	specObject := nm.NewObject()
+	specObject.Set(
+		nm.InheritedKey("template", nm.KeyOptMixin(true)),
+		templateObject,
+	)
+
+	o.Set(
+		nm.InheritedKey("spec", nm.KeyOptMixin(true)),
+		specObject,
+	)
+
+	return o
+}
+
+func createMapContainersWithName() *nm.Local {
+	c1Binary := nm.NewBinary(
+		nm.ApplyCall("std.objectHas", nm.NewVar("c"), nm.NewStringDouble("name")),
+		nm.ApplyCall("inNameSet", nm.NewCall("c.name")),
+		nm.BopAnd,
+	)
+
+	c1True := nm.ApplyCall("f", nm.NewVar("c"))
+	c1False := nm.NewVar("c")
+
+	c1 := nm.NewConditional(c1Binary, c1True, c1False)
+
+	apply := nm.NewApply(c1, []nm.Noder{nm.NewVar("c")}, nil)
+
+	runMap := nm.ApplyCall("self.mapContainers", apply)
+
+	a := nm.NewVar("nameSet")
+	b := nm.ApplyCall("std.set", nm.NewArray([]nm.Noder{nm.NewVar("name")}))
+
+	inNameSet := nm.NewLocal(
+		"inNameSet",
+		nm.NewFunction([]string{"name"}, genIsIntersection(a, b)),
+		runMap,
+	)
+	nameSet := nm.NewLocal("nameSet", setArray("names"), inNameSet)
+
+	return nameSet
+}
+
+func setArray(varName string) *nm.Conditional {
+	bin := nm.NewBinary(
+		nm.ApplyCall("std.type", nm.NewVar(varName)),
+		nm.NewStringDouble("array"),
+		nm.BopEqual,
+	)
+
+	tBranch := nm.ApplyCall("std.set", nm.NewVar(varName))
+	fBranch := nm.ApplyCall("std.set", nm.NewArray([]nm.Noder{nm.NewVar(varName)}))
+
+	return nm.NewConditional(bin, tBranch, fBranch)
+}
+
+func genIsIntersection(a, b nm.Noder) *nm.Binary {
+	intersection := nm.ApplyCall("std.Inter", a, b)
+	checkLen := nm.ApplyCall("std.length", intersection)
+
+	return nm.NewBinary(
+		checkLen,
+		nm.NewInt(0),
+		nm.BopGreater,
+	)
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/field.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/field.go
new file mode 100644
index 0000000000000000000000000000000000000000..7d8ce3fe0a319ca98b14614ee4bda2b5835f2526
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/field.go
@@ -0,0 +1,76 @@
+package ksonnet
+
+// Field is a Kubernetes field.
+type Field struct {
+	kind        string
+	description string
+	properties  map[string]Property
+	version     string
+	group       string
+	codebase    string
+	identifier  string
+}
+
+var _ Object = (*Field)(nil)
+
+// NewField creates an instance of Field.
+func NewField(id, desc, codebase, group, ver, kind string, props map[string]Property) *Field {
+	return &Field{
+		identifier:  id,
+		description: desc,
+		group:       group,
+		codebase:    codebase,
+		version:     ver,
+		kind:        kind,
+		properties:  props,
+	}
+}
+
+// Kind is the kind for this field.
+func (f *Field) Kind() string {
+	return f.kind
+}
+
+// Version is the version for this field.
+func (f *Field) Version() string {
+	return f.version
+}
+
+// Codebase is the codebase for this field.
+func (f *Field) Codebase() string {
+	return f.codebase
+}
+
+// Group is the group for this field.
+func (f *Field) Group() string {
+	if f.group == "" {
+		return "core"
+	}
+
+	return f.group
+}
+
+// QualifiedGroup is the group for this field.
+func (f *Field) QualifiedGroup() string {
+	return f.Group()
+}
+
+// Description is the description for this field.
+func (f *Field) Description() string {
+	return f.description
+}
+
+// Identifier is the identifier for this field.
+func (f *Field) Identifier() string {
+	return f.identifier
+}
+
+// IsType returns if this item is a type. It always returns false.
+func (f *Field) IsType() bool {
+	return false
+}
+
+// Properties are the properties for this field.
+func (f *Field) Properties() map[string]Property {
+	return f.properties
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/group.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/group.go
new file mode 100644
index 0000000000000000000000000000000000000000..07d4427752434ee9c5e5502b330649320e241c15
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/group.go
@@ -0,0 +1,64 @@
+package ksonnet
+
+import (
+	"sort"
+
+	nm "github.com/ksonnet/ksonnet-lib/ksonnet-gen/nodemaker"
+)
+
+// Group is group of definitions.
+type Group struct {
+	versions map[string]*Version
+	name     string
+}
+
+// NewGroup creates an instance of Group.
+func NewGroup(name string) *Group {
+	return &Group{
+		versions: make(map[string]*Version),
+		name:     name,
+	}
+}
+
+// Name is the name of the group.
+func (g *Group) Name() string {
+	return g.name
+}
+
+// Versions returns the versions available for this group.
+func (g *Group) Versions() []Version {
+	var names []string
+	for name := range g.versions {
+		names = append(names, name)
+	}
+
+	sort.Strings(names)
+
+	var versions []Version
+	for _, name := range names {
+		versions = append(versions, *g.versions[name])
+	}
+
+	return versions
+}
+
+// AddResource adds a resource to a version.
+func (g *Group) AddResource(r Object) {
+	name := r.Version()
+	if name == "" {
+		return
+	}
+
+	v, ok := g.versions[name]
+	if !ok {
+		v = NewVersion(name, r.QualifiedGroup())
+		g.versions[name] = v
+	}
+
+	v.AddResource(r)
+}
+
+// Node returns an ast node for this group.
+func (g *Group) Node() *nm.Object {
+	return nm.NewObject()
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/ksonnet.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/ksonnet.go
new file mode 100644
index 0000000000000000000000000000000000000000..677ed0a875f4015fd5e8a07d741b6be3b2e8650d
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/ksonnet.go
@@ -0,0 +1,84 @@
+package ksonnet
+
+import (
+	"bytes"
+
+	"github.com/ksonnet/ksonnet-lib/ksonnet-gen/kubespec"
+	"github.com/ksonnet/ksonnet-lib/ksonnet-gen/printer"
+	"github.com/pkg/errors"
+)
+
+// Lib is a ksonnet lib.
+type Lib struct {
+	K8s        []byte
+	Extensions []byte
+	Version    string
+}
+
+// GenerateLib generates ksonnet lib.
+func GenerateLib(source string) (*Lib, error) {
+	apiSpec, checksum, err := kubespec.Import(source)
+	if err != nil {
+		return nil, errors.Wrap(err, "import Kubernetes spec")
+	}
+
+	c, err := NewCatalog(apiSpec, CatalogOptChecksum(checksum))
+	if err != nil {
+		return nil, errors.Wrap(err, "create ksonnet catalog")
+	}
+
+	k8s, err := createK8s(c)
+	if err != nil {
+		return nil, errors.Wrap(err, "create k8s.libsonnet")
+	}
+
+	k, err := createK(c)
+	if err != nil {
+		return nil, errors.Wrap(err, "create k.libsonnet")
+	}
+
+	lib := &Lib{
+		K8s:        k8s,
+		Extensions: k,
+		Version:    c.apiVersion.String(),
+	}
+
+	return lib, nil
+}
+
+func createK8s(c *Catalog) ([]byte, error) {
+	doc, err := NewDocument(c)
+	if err != nil {
+		return nil, errors.Wrapf(err, "create document")
+	}
+
+	node, err := doc.Node()
+	if err != nil {
+		return nil, errors.Wrapf(err, "build document node")
+	}
+
+	var buf bytes.Buffer
+
+	if err := printer.Fprint(&buf, node.Node()); err != nil {
+		return nil, errors.Wrap(err, "print AST")
+	}
+
+	return buf.Bytes(), nil
+}
+
+func createK(c *Catalog) ([]byte, error) {
+	e := NewExtension(c)
+
+	node, err := e.Node()
+	if err != nil {
+		return nil, errors.Wrapf(err, "build extension node")
+	}
+
+	var buf bytes.Buffer
+
+	if err := printer.Fprint(&buf, node.Node()); err != nil {
+		return nil, errors.Wrap(err, "print AST")
+	}
+
+	return buf.Bytes(), nil
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/object.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/object.go
new file mode 100644
index 0000000000000000000000000000000000000000..471b6565eb440235a81321c1bea6a54fd165608f
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/object.go
@@ -0,0 +1,94 @@
+package ksonnet
+
+// Object is an object that can be turned into a node by APIObject.
+type Object interface {
+	Kind() string
+	Description() string
+	IsType() bool
+	Properties() map[string]Property
+	Version() string
+	Group() string
+	Codebase() string
+	QualifiedGroup() string
+	Identifier() string
+}
+
+// Property is a field in a resource
+type Property interface {
+	Description() string
+	Name() string
+	Ref() string
+}
+
+// LiteralField is a literal field. (e.g. string, number, int, array)
+type LiteralField struct {
+	name        string
+	fieldType   string
+	description string
+	ref         string
+}
+
+var _ Property = (*LiteralField)(nil)
+
+// NewLiteralField creates an instance of LiteralField.
+func NewLiteralField(name, fieldType, description, ref string) *LiteralField {
+	return &LiteralField{
+		name:        name,
+		fieldType:   fieldType,
+		description: description,
+		ref:         ref,
+	}
+}
+
+// FieldType returns the field type of the LiteralField.
+func (f *LiteralField) FieldType() string {
+	return f.fieldType
+}
+
+// Name returns the name of the LiteralField.
+func (f *LiteralField) Name() string {
+	return f.name
+}
+
+// Description returns the description of the LiteralField.
+func (f *LiteralField) Description() string {
+	return f.description
+}
+
+// Ref returns the ref of the LiteralField.
+func (f *LiteralField) Ref() string {
+	return f.ref
+}
+
+// ReferenceField is a reference field.
+type ReferenceField struct {
+	name        string
+	description string
+	ref         string
+}
+
+var _ Property = (*ReferenceField)(nil)
+
+// NewReferenceField creates an instance of ReferenceField.
+func NewReferenceField(name, description, ref string) *ReferenceField {
+	return &ReferenceField{
+		name:        name,
+		description: description,
+		ref:         ref,
+	}
+}
+
+// Name returns the name of the ReferenceField.
+func (f *ReferenceField) Name() string {
+	return f.name
+}
+
+// Description returns the description of the ReferenceField.
+func (f *ReferenceField) Description() string {
+	return f.description
+}
+
+// Ref returns the defintion this ReferenceField represents.
+func (f *ReferenceField) Ref() string {
+	return f.ref
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/paths.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/paths.go
new file mode 100644
index 0000000000000000000000000000000000000000..55200d1f0283722403affdf4df06184796b92a7e
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/paths.go
@@ -0,0 +1,69 @@
+package ksonnet
+
+import (
+	"github.com/go-openapi/spec"
+	"github.com/pkg/errors"
+)
+
+func parsePaths(apiSpec *spec.Swagger) (map[string]Component, error) {
+	m := make(map[string]Component)
+
+	if apiSpec.Paths == nil {
+		return nil, errors.New("api spec has zero paths")
+	}
+	paths := apiSpec.Paths.Paths
+	for _, pathItem := range paths {
+		verbs := []*spec.Operation{pathItem.Post, pathItem.Patch, pathItem.Put}
+		for _, verb := range verbs {
+			if verb == nil {
+				continue
+			}
+
+			var body *spec.Parameter
+			for _, param := range verb.Parameters {
+				if param.Name == "body" {
+					body = &param
+				}
+			}
+
+			if body == nil {
+				continue
+			}
+
+			ref := extractRef(*body.Schema)
+
+			component, exists, err := pathExtensionComponent(verb.Extensions)
+			if err != nil {
+				return nil, errors.Wrapf(err, "extract component for %s", ref)
+			}
+
+			if exists {
+				m[ref] = component
+			}
+
+		}
+	}
+
+	return m, nil
+}
+
+// pathExtensionComponent generates a component from a method tpe extension
+func pathExtensionComponent(extensions spec.Extensions) (Component, bool, error) {
+	for x, v := range extensions {
+		if x == extensionGroupVersionKind {
+			gvk, ok := v.(map[string]interface{})
+			if !ok {
+				return Component{}, false, errors.New("gvk extension was invalid")
+			}
+
+			component := Component{
+				Group:   gvk["group"].(string),
+				Version: gvk["version"].(string),
+				Kind:    gvk["kind"].(string),
+			}
+			return component, true, nil
+		}
+	}
+
+	return Component{}, false, nil
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/properties.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/properties.go
new file mode 100644
index 0000000000000000000000000000000000000000..f69a929ff74f66fd294205f3b9c34239c5052b1c
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/properties.go
@@ -0,0 +1,95 @@
+package ksonnet
+
+import (
+	"strings"
+
+	"github.com/go-openapi/spec"
+	"github.com/pkg/errors"
+)
+
+var (
+	recursiveRefs = []string{
+		"io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps",
+	}
+)
+
+func extractProperties(c *Catalog, properties map[string]spec.Schema, required []string) (map[string]Property, error) {
+	if c == nil {
+		return nil, errors.New("catalog is nil")
+	}
+
+	out := make(map[string]Property)
+
+	for name, schema := range properties {
+		if isSkippedProperty(name, schema) {
+			if !stringInSlice(name, required) {
+				continue
+			}
+		}
+
+		ref := extractRef(schema)
+
+		if ref != "" && stringInSlice(ref, recursiveRefs) {
+			out[name] = NewLiteralField(name, "object", schema.Description, ref)
+			continue
+		}
+
+		// literal
+		if t := schema.Type; len(t) == 1 {
+			out[name] = buildLiteralField(t[0], name, schema)
+			continue
+		}
+
+		ifr, err := c.isFormatRef(ref)
+		if err != nil {
+			return nil, errors.Wrap(err, "check for format ref")
+		}
+
+		if ifr {
+			// don't have to check for existence here because isFormatRef does the same thing
+			formatSchema := c.apiSpec.Definitions[ref]
+			out[name] = buildLiteralField(fieldType(formatSchema), name, schema)
+			continue
+		}
+
+		// must be a mixin
+		f := NewReferenceField(name, schema.Description, ref)
+		out[name] = f
+	}
+
+	return out, nil
+}
+
+func buildLiteralField(fieldType, name string, schema spec.Schema) *LiteralField {
+	var itemRef string
+	if schema.Items != nil && schema.Items.Schema != nil {
+		itemRef = extractRef(*schema.Items.Schema)
+	}
+
+	return NewLiteralField(name, fieldType, schema.Description, itemRef)
+}
+
+func isSkippedProperty(name string, schema spec.Schema) bool {
+	if stringInSlice(name, blockedPropertyNames) {
+		return true
+	}
+
+	if strings.Contains(strings.ToLower(schema.Description), "read-only") {
+		return true
+	}
+
+	ref := extractRef(schema)
+	if stringInSlice(ref, blockedReferences) {
+		return true
+	}
+
+	return false
+}
+
+func fieldType(schema spec.Schema) string {
+	if t := schema.Type; len(t) == 1 {
+		return t[0]
+	}
+
+	return ""
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/renderer.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/renderer.go
new file mode 100644
index 0000000000000000000000000000000000000000..b08d237b6191fcbe0f1c6d54cea20024d27122b8
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/renderer.go
@@ -0,0 +1,366 @@
+package ksonnet
+
+import (
+	"fmt"
+	"sort"
+	"strings"
+
+	nm "github.com/ksonnet/ksonnet-lib/ksonnet-gen/nodemaker"
+	"github.com/pkg/errors"
+)
+
+// renderer is an item that can be rendered.
+type renderer interface {
+	Render(parent *nm.Object) error
+}
+
+type baseRenderer struct {
+	name        string
+	description string
+	parent      string
+	ref         string
+}
+
+func newBaseRenderer(field Property, parent string) baseRenderer {
+	return baseRenderer{
+		name:        field.Name(),
+		description: field.Description(),
+		parent:      parent,
+		ref:         field.Ref(),
+	}
+}
+
+func (r *baseRenderer) setter() string {
+	return fieldName(r.name, false)
+}
+
+func (r *baseRenderer) mixin() string {
+	return fieldName(r.name, true)
+}
+
+// LiteralFieldRenderer renders a literal field.
+type LiteralFieldRenderer struct {
+	lf         *LiteralField
+	parentName string
+}
+
+// NewLiteralFieldRenderer creates an instance of LiteralField.
+func NewLiteralFieldRenderer(lf *LiteralField, parentName string) *LiteralFieldRenderer {
+	return &LiteralFieldRenderer{
+		lf:         lf,
+		parentName: parentName,
+	}
+}
+
+// Render renders the literal field in the container.
+func (r *LiteralFieldRenderer) Render(container *nm.Object) error {
+	var rndr renderer
+
+	switch ft := r.lf.FieldType(); ft {
+	case "array":
+		rndr = NewArrayRenderer(r.lf, r.parentName)
+	case "object":
+		rndr = NewObjectRenderer(r.lf, r.parentName)
+	case "string", "boolean", "integer", "number":
+		rndr = NewItemRenderer(r.lf, r.parentName)
+	default:
+		return errors.Errorf("unknown literal field type %s", ft)
+	}
+
+	return rndr.Render(container)
+}
+
+// ReferenceRenderer renders a reference field.
+type ReferenceRenderer struct {
+	baseRenderer
+	rf *ReferenceField
+	tl typeLookup
+}
+
+// NewReferenceRenderer creates an instance of ReferenceRenderer.
+func NewReferenceRenderer(rf *ReferenceField, tl typeLookup, parent string) *ReferenceRenderer {
+	return &ReferenceRenderer{
+		baseRenderer: newBaseRenderer(rf, parent),
+		tl:           tl,
+		rf:           rf,
+	}
+}
+
+// Render renders the reference in the container.
+func (r *ReferenceRenderer) Render(container *nm.Object) error {
+	name := r.rf.Name()
+	desc := r.rf.Description()
+
+	mo := nm.NewObject()
+	mixinPreamble(mo, r.parent, name)
+
+	ref := r.rf.Ref()
+
+	ty, err := r.tl.Field(ref)
+	if err != nil {
+		return errors.Wrapf(err, "fetch type %s", ref)
+	}
+
+	renderFields(r.tl, mo, name, ty.Properties())
+
+	formattedName := FormatKind(r.rf.Name())
+
+	container.Set(nm.NewKey(formattedName, nm.KeyOptComment(desc)), mo)
+	_ = genTypeAliasEntry(container, name, ref)
+
+	return nil
+}
+
+// ObjectRenderer renders an object field.
+type ObjectRenderer struct {
+	baseRenderer
+}
+
+// NewObjectRenderer creates an instance of ObjectRenderer
+func NewObjectRenderer(field Property, parent string) *ObjectRenderer {
+	return &ObjectRenderer{
+		baseRenderer: newBaseRenderer(field, parent),
+	}
+}
+
+// Render renders the object field in the container.
+func (r *ObjectRenderer) Render(container *nm.Object) error {
+	wrapper := mixinName(r.parent)
+	setterFn := createObjectWithField(r.name, wrapper, false)
+	setProperty(container, r.setter(), r.description, []string{FormatKind(r.name)}, setterFn)
+
+	mixinFn := createObjectWithField(r.name, wrapper, true)
+	setProperty(container, r.mixin(), r.description, []string{FormatKind(r.name)}, mixinFn)
+
+	_ = genTypeAliasEntry(container, r.name, r.ref)
+
+	return nil
+}
+
+// ItemRenderer renders items.
+type ItemRenderer struct {
+	baseRenderer
+}
+
+var _ renderer = (*ItemRenderer)(nil)
+
+// NewItemRenderer creates an instance of ItemRenderer.
+func NewItemRenderer(f Property, parent string) *ItemRenderer {
+	return &ItemRenderer{baseRenderer: newBaseRenderer(f, parent)}
+}
+
+// Render renders an item in its parent object.
+func (r *ItemRenderer) Render(parent *nm.Object) error {
+	noder := createObjectWithField(r.name, mixinName(r.parent), false)
+	setProperty(parent, r.setter(), r.description, []string{FormatKind(r.name)}, noder)
+
+	_ = genTypeAliasEntry(parent, r.name, r.ref)
+	return nil
+}
+
+// ArrayRenderer renders arrays.
+type ArrayRenderer struct {
+	baseRenderer
+}
+
+// NewArrayRenderer creates an instance of ArrayRenderer.
+func NewArrayRenderer(f Property, parent string) *ArrayRenderer {
+	return &ArrayRenderer{baseRenderer: newBaseRenderer(f, parent)}
+}
+
+// Render renders an item in its parent object.
+func (r *ArrayRenderer) Render(container *nm.Object) error {
+	wrapper := mixinName(r.parent)
+	setterFn := convertToArray(r.name, wrapper, false)
+	setProperty(container, r.setter(), r.description, []string{FormatKind(r.name)}, setterFn)
+
+	mixinFn := convertToArray(r.name, wrapper, true)
+	setProperty(container, r.mixin(), r.description, []string{FormatKind(r.name)}, mixinFn)
+
+	_ = genTypeAliasEntry(container, r.name, r.ref)
+	return nil
+}
+
+func convertToArray(varName, parent string, mixin bool) nm.Noder {
+	apply := nm.NewApply(
+		nm.NewCall("std.type"),
+		[]nm.Noder{nm.NewVar(FormatKind(varName))},
+		nil)
+
+	test := nm.NewBinary(apply, nm.NewStringDouble("array"), nm.BopEqual)
+
+	var trueBranch nm.Noder
+	var falseBranch nm.Noder
+
+	trueO := nm.OnelineObject()
+	trueO.Set(
+		nm.InheritedKey(varName, nm.KeyOptMixin(mixin)),
+		nm.NewVar(FormatKind(varName)))
+
+	falseO := nm.OnelineObject()
+	falseO.Set(
+		nm.InheritedKey(varName, nm.KeyOptMixin(mixin)),
+		nm.NewArray([]nm.Noder{nm.NewVar(FormatKind(varName))}))
+
+	if parent == "" {
+		trueBranch = trueO
+		falseBranch = falseO
+	} else {
+		trueBranch = nm.NewApply(nm.NewCall(parent), []nm.Noder{trueO}, nil)
+		falseBranch = nm.NewApply(nm.NewCall(parent), []nm.Noder{falseO}, nil)
+	}
+
+	return nm.NewConditional(test, trueBranch, falseBranch)
+}
+
+// createObjectWithField creates an object with a field. Creates {field: field} or {field+: field}
+// if mixin. If it has a parent, it create __parentNameMixin({field: field}).
+func createObjectWithField(name, parentName string, mixin bool) nm.Noder {
+	var noder nm.Noder
+	io := nm.OnelineObject()
+	io.Set(nm.InheritedKey(name, nm.KeyOptMixin(mixin)), nm.NewVar(FormatKind(name)))
+
+	if parentName == "" {
+		noder = io
+	} else {
+		noder = nm.NewApply(nm.NewCall(parentName), []nm.Noder{io}, nil)
+	}
+
+	return noder
+}
+
+func setProperty(o *nm.Object, fnName, desc string, args []string, node nm.Noder) {
+	node = nm.NewBinary(&nm.Self{}, node, nm.BopPlus)
+	key := nm.FunctionKey(fnName, args, nm.KeyOptComment(desc))
+	o.Set(key, node)
+}
+
+func mixinPreamble(o *nm.Object, parent, name string) error {
+	if o == nil {
+		return errors.New("parent object is nil")
+	}
+	name = FormatKind(name)
+
+	formattedName := mixinName(name)
+
+	var noder nm.Noder
+
+	io := nm.OnelineObject()
+	io.Set(nm.InheritedKey(name, nm.KeyOptMixin(true)), nm.NewVar(name))
+
+	if parent == "" {
+		noder = io
+	} else {
+		noder = nm.ApplyCall(mixinName(parent), io)
+	}
+
+	o.Set(nm.LocalKey(formattedName, nm.KeyOptParams([]string{name})), noder)
+
+	miFn := nm.NewCall(formattedName)
+	o.Set(nm.FunctionKey("mixinInstance", []string{name}), nm.NewApply(miFn, []nm.Noder{nm.NewVar(name)}, nil))
+
+	return nil
+}
+
+func genTypeAliasEntry(container *nm.Object, name, refName string) error {
+	if refName == "" {
+		return errors.New("ref name is blank")
+	}
+
+	rd, err := ParseDescription(refName)
+	if err != nil {
+		return errors.Wrapf(err, "parse ref name from %q and %q", name, refName)
+	}
+
+	if rd.Group == "" {
+		rd.Group = "core"
+	}
+
+	if rd.Version == "" {
+		return errors.Errorf("there is no version in the ref name for %q and %q",
+			name, refName)
+	}
+
+	kind := FormatKind(rd.Kind)
+	path := []string{"hidden", rd.Group, rd.Version, kind}
+	location := strings.Join(path, ".")
+
+	typeAliasName := fmt.Sprintf("%sType", name)
+
+	c := nm.NewCall(location)
+
+	container.Set(nm.NewKey(typeAliasName), c)
+
+	return nil
+}
+
+// Generates a field name.
+func fieldName(name string, isMixin bool) string {
+	var out string
+
+	name = FormatKind(name)
+
+	out = fmt.Sprintf("with%s", strings.Title(name))
+	if isMixin {
+		return fmt.Sprintf("%s%s", out, "Mixin")
+	}
+
+	return out
+}
+
+func mixinName(name string) string {
+	if name == "" {
+		return ""
+	}
+
+	name = FormatKind(name)
+
+	return fmt.Sprintf("__%sMixin", name)
+}
+
+// typeLookup can look up types by id.
+type typeLookup interface {
+	Field(id string) (*Field, error)
+}
+
+type renderFieldsFn func(tl typeLookup, parent *nm.Object, parentName string, props map[string]Property) error
+
+// renderFields renders fields from a property map.
+func renderFields(tl typeLookup, parent *nm.Object, parentName string, props map[string]Property) error {
+	container := parent
+	if parentName == "" {
+		container = nm.NewObject()
+	}
+
+	var names []string
+	for name := range props {
+		names = append(names, name)
+	}
+
+	sort.Strings(names)
+
+	for _, name := range names {
+		field := props[name]
+
+		switch t := field.(type) {
+		case *LiteralField:
+			r := NewLiteralFieldRenderer(t, parentName)
+			if err := r.Render(parent); err != nil {
+				return errors.Wrap(err, "render literal field")
+			}
+		case *ReferenceField:
+			r := NewReferenceRenderer(t, tl, parentName)
+			if err := r.Render(container); err != nil {
+				return errors.Wrap(err, "render reference field")
+			}
+		default:
+			return errors.Errorf("unknown field type %T", t)
+		}
+	}
+
+	if parentName == "" {
+		parent.Set(nm.NewKey("mixin"), container)
+	}
+
+	return nil
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/strings.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/strings.go
new file mode 100644
index 0000000000000000000000000000000000000000..0891d38374a04204421c8aa1e51cff7604ddb090
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/strings.go
@@ -0,0 +1,103 @@
+package ksonnet
+
+import (
+	"bytes"
+	"strings"
+	"unicode"
+)
+
+var (
+	jsonnetKeywords = []string{"assert", "else", "error", "false", "for", "function", "if",
+		"import", "importstr", "in", "null", "tailstrict", "then", "self", "super",
+		"true"}
+)
+
+// camelCase converts a string to camel case.
+func camelCase(in string) string {
+	out := ""
+
+	for i, r := range in {
+		if i == 0 {
+			out += strings.ToLower(string(r))
+			continue
+		}
+
+		out += string(r)
+
+	}
+
+	return out
+}
+
+// stringInSlice returns true if the string is in the slice.
+func stringInSlice(a string, list []string) bool {
+	for _, b := range list {
+		if b == a {
+			return true
+		}
+	}
+	return false
+}
+
+// capitalizer adjusts the case of terms found in a string.
+func toLower(b byte) byte {
+	return byte(unicode.ToLower(rune(b)))
+}
+
+func isUpper(b byte) bool {
+	return unicode.IsUpper(rune(b))
+}
+
+// capitalize adjusts the case of terms found in a string. It will convert `HTTPHeader` into
+// `HttpHeader`.
+func capitalize(in string) string {
+	l := len(in) - 1
+
+	if l == 0 {
+		// nothing to do when there is a one character strings
+		return in
+	}
+
+	var b bytes.Buffer
+	b.WriteByte(in[0])
+
+	for i := 1; i <= l; i++ {
+		if isUpper(in[i-1]) {
+			if i < l {
+				if isUpper(in[i+1]) || (isUpper(in[i]) && i+1 == l) {
+					b.WriteByte(toLower(in[i]))
+				} else {
+					b.WriteByte(in[i])
+				}
+			} else if i == l && isUpper(in[i]) {
+				b.WriteByte(toLower(in[i]))
+			} else {
+				b.WriteByte(in[i])
+			}
+		} else {
+			b.WriteByte(in[i])
+		}
+	}
+
+	return b.String()
+}
+
+// FormatKind formats a string in kind format. i.e camel case with jsonnet keywords massaged.
+func FormatKind(s string) string {
+	if strings.ToLower(s) == "local" {
+		return "localStorage"
+	}
+
+	if strings.HasPrefix(s, "$") {
+		s = "dollar" + strings.Title(strings.TrimPrefix(s, "$"))
+		return s
+	}
+	s = capitalize(s)
+	s = camelCase(s)
+
+	if stringInSlice(s, jsonnetKeywords) {
+		s = s + "Param"
+	}
+
+	return s
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/type.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/type.go
new file mode 100644
index 0000000000000000000000000000000000000000..96c06204dd400335f29df4f8fda57dddc6355e74
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/type.go
@@ -0,0 +1,74 @@
+package ksonnet
+
+// Type is a Kubernetes kind.
+type Type struct {
+	description string
+	properties  map[string]Property
+	component   Component
+	group       string
+	codebase    string
+	identifier  string
+}
+
+var _ Object = (*Type)(nil)
+
+// NewType creates an instance of Type.
+func NewType(identifier, description, codebase, group string, component Component, props map[string]Property) Type {
+	return Type{
+		description: description,
+		group:       group,
+		codebase:    codebase,
+		component:   component,
+		properties:  props,
+		identifier:  identifier,
+	}
+}
+
+// Kind is the kind for this type
+func (t *Type) Kind() string {
+	return t.component.Kind
+}
+
+// Version is the version for this type
+func (t *Type) Version() string {
+	return t.component.Version
+}
+
+// Codebase is the codebase for this field.
+func (t *Type) Codebase() string {
+	return t.codebase
+}
+
+// Group is the group for this type
+func (t *Type) Group() string {
+	if t.group == "" {
+		return "core"
+	}
+
+	return t.group
+}
+
+// QualifiedGroup is the group for this type
+func (t *Type) QualifiedGroup() string {
+	return t.component.Group
+}
+
+// Description is description for this type
+func (t *Type) Description() string {
+	return t.description
+}
+
+// Identifier is identifier for this type
+func (t *Type) Identifier() string {
+	return t.identifier
+}
+
+// IsType returns if this item is a type. It always returns true.
+func (t *Type) IsType() bool {
+	return true
+}
+
+// Properties are the properties for this type.
+func (t *Type) Properties() map[string]Property {
+	return t.properties
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/version.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/version.go
new file mode 100644
index 0000000000000000000000000000000000000000..8c2809a6818e43d656f3bbba92f5255d300cbebc
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet/version.go
@@ -0,0 +1,78 @@
+package ksonnet
+
+import (
+	"fmt"
+	"sort"
+
+	"github.com/google/go-jsonnet/ast"
+	nm "github.com/ksonnet/ksonnet-lib/ksonnet-gen/nodemaker"
+)
+
+// Version is an API version.
+type Version struct {
+	name  string
+	group string
+
+	resources []*APIObject
+}
+
+// NewVersion creates an instance of Version.
+func NewVersion(name, group string) *Version {
+	v := &Version{
+		name:      name,
+		group:     group,
+		resources: make([]*APIObject, 0),
+	}
+
+	return v
+}
+
+// APIObjects returns a slice of APIObjects sorted by name.
+func (v *Version) APIObjects() []APIObject {
+	var objects []APIObject
+	for _, resource := range v.resources {
+		objects = append(objects, *resource)
+	}
+
+	sort.Slice(objects, func(i, j int) bool {
+		return objects[i].Kind() < objects[j].Kind()
+	})
+
+	return objects
+}
+
+// Name is the name of the version.
+func (v *Version) Name() string {
+	return v.name
+}
+
+// AddResource adds a resource to the version.
+func (v *Version) AddResource(resource Object) {
+	ao := NewAPIObject(resource)
+	v.resources = append(v.resources, ao)
+}
+
+// APIVersion returns the version.
+func (v *Version) APIVersion() string {
+	if v.group == "core" || v.group == "" {
+		return v.name
+	}
+	return fmt.Sprintf("%s/%s", v.group, v.name)
+}
+
+// Node returns an ast node for this version.
+func (v *Version) Node() *nm.Object {
+	o := nm.NewObject()
+
+	avo := nm.OnelineObject()
+	avo.Set(
+		nm.NewKey(
+			"apiVersion",
+			nm.KeyOptCategory(ast.ObjectFieldID),
+			nm.KeyOptVisibility(ast.ObjectFieldInherit)),
+		nm.NewStringDouble(v.APIVersion()))
+
+	o.Set(nm.LocalKey("apiVersion"), avo)
+
+	return o
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/kubespec/importer.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/kubespec/importer.go
new file mode 100644
index 0000000000000000000000000000000000000000..c03c99ff5ab3886ab5bec3295d5aa10cf5900591
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/kubespec/importer.go
@@ -0,0 +1,41 @@
+package kubespec
+
+import (
+	"crypto/sha256"
+	"encoding/json"
+	"fmt"
+
+	"github.com/go-openapi/spec"
+	"github.com/go-openapi/swag"
+	"github.com/pkg/errors"
+)
+
+// Import imports an OpenAPI swagger schema.
+func Import(path string) (*spec.Swagger, string, error) {
+	b, err := swag.LoadFromFileOrHTTP(path)
+	if err != nil {
+		return nil, "", errors.Wrap(err, "load schema from path")
+	}
+
+	h := sha256.New()
+	h.Write(b)
+
+	checksum := fmt.Sprintf("%x", h.Sum(nil))
+
+	spec, err := CreateAPISpec(b)
+	if err != nil {
+		return nil, "", err
+	}
+
+	return spec, checksum, nil
+}
+
+// CreateAPISpec a swagger file into a *spec.Swagger.
+func CreateAPISpec(b []byte) (*spec.Swagger, error) {
+	var apiSpec spec.Swagger
+	if err := json.Unmarshal(b, &apiSpec); err != nil {
+		return nil, errors.Wrap(err, "parse swagger JSON")
+	}
+
+	return &apiSpec, nil
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/kubespec/parsing.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/kubespec/parsing.go
index d111bd5ccd168eb03a5b48e9697c26ec81f37dbe..53e9519ce2588d7f2011b4449c12589fb4d07a62 100644
--- a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/kubespec/parsing.go
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/kubespec/parsing.go
@@ -17,7 +17,7 @@ func (dn *DefinitionName) Parse() (*ParsedDefinitionName, error) {
 
 	desc, err := describeDefinition(name)
 	if err != nil {
-		return nil, fmt.Errorf("describe definition: #v", err)
+		return nil, fmt.Errorf("describe definition: %#v", err)
 	}
 
 	pd := ParsedDefinitionName{
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/nodemaker/nodemaker.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/nodemaker/nodemaker.go
new file mode 100644
index 0000000000000000000000000000000000000000..6c6f33fd6866c2bbb3fee053caf93cf4acbf2b99
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/nodemaker/nodemaker.go
@@ -0,0 +1,941 @@
+package nodemaker
+
+import (
+	"fmt"
+	"regexp"
+	"sort"
+	"strconv"
+	"strings"
+
+	"github.com/google/go-jsonnet/ast"
+	"github.com/ksonnet/ksonnet-lib/ksonnet-gen/astext"
+	"github.com/pkg/errors"
+)
+
+// Noder is an entity that can be converted to a jsonnet node.
+type Noder interface {
+	Node() ast.Node
+}
+
+type field struct {
+	key   Key
+	value Noder
+}
+
+// ObjectOptOneline is a functional option which sets the object's oneline status.
+func ObjectOptOneline(oneline bool) ObjectOpt {
+	return func(o *Object) {
+		o.Oneline = oneline
+	}
+}
+
+// ObjectOpt is a functional option for Object.
+type ObjectOpt func(*Object)
+
+// Object is an item that can have multiple keys with values.
+type Object struct {
+	Oneline bool
+	fields  map[string]Noder
+	keys    map[string]Key
+	keyList []string
+}
+
+var _ Noder = (*Object)(nil)
+
+// KVFromMap creates a object using a map.
+// nolint: gocyclo
+func KVFromMap(m map[string]interface{}) (*Object, error) {
+	if m == nil {
+		return nil, errors.New("map is nil")
+	}
+
+	var names []string
+	for name := range m {
+		names = append(names, name)
+	}
+	sort.Strings(names)
+
+	o := NewObject()
+
+	for _, name := range names {
+		switch t := m[name].(type) {
+		case string, float64, int, bool:
+			val, err := convertValueToNoder(t)
+			if err != nil {
+				return nil, err
+			}
+			o.Set(InheritedKey(name), val)
+		case []interface{}:
+			var elements []Noder
+			for _, val := range t {
+				noder, err := convertValueToNoder(val)
+				if err != nil {
+					return nil, err
+				}
+
+				elements = append(elements, noder)
+			}
+			array := NewArray(elements)
+			o.Set(InheritedKey(name), array)
+		case map[interface{}]interface{}:
+			newMap, err := convertMapToStringKey(t)
+			if err != nil {
+				return nil, err
+			}
+			child, err := KVFromMap(newMap)
+			if err != nil {
+				return nil, err
+			}
+
+			o.Set(InheritedKey(name), child)
+		default:
+			return nil, errors.Errorf("unsupported type %T", t)
+		}
+	}
+
+	return o, nil
+}
+
+func convertMapToStringKey(m map[interface{}]interface{}) (map[string]interface{}, error) {
+	newMap := make(map[string]interface{})
+	for k := range m {
+		s, ok := k.(string)
+		if !ok {
+			return nil, errors.New("map key is not a string")
+		}
+
+		newMap[s] = m[s]
+	}
+
+	return newMap, nil
+}
+
+func convertValueToNoder(val interface{}) (Noder, error) {
+	switch t := val.(type) {
+	case string:
+		return NewStringDouble(t), nil
+	case float64:
+		return NewFloat(t), nil
+	case int:
+		return NewInt(t), nil
+	case bool:
+		return NewBoolean(t), nil
+	default:
+		return nil, errors.Errorf("unsupported type %T", t)
+	}
+}
+
+// NewObject creates an Object. ObjectOpt functional arguments can be used to configure the
+// newly generated key.
+func NewObject(opts ...ObjectOpt) *Object {
+	o := &Object{
+		fields:  make(map[string]Noder),
+		keys:    make(map[string]Key),
+		keyList: make([]string, 0),
+	}
+
+	for _, opt := range opts {
+		opt(o)
+	}
+
+	return o
+}
+
+// OnelineObject is a convenience method for creating a online object.
+func OnelineObject(opts ...ObjectOpt) *Object {
+	opts = append(opts, ObjectOptOneline(true))
+	return NewObject(opts...)
+}
+
+// Set sets a field with a value.
+func (o *Object) Set(key Key, value Noder) error {
+	name := key.name
+
+	if _, ok := o.keys[name]; ok {
+		return errors.Errorf("field %q already exists in the object", name)
+	}
+
+	o.keys[name] = key
+	o.fields[name] = value
+	o.keyList = append(o.keyList, name)
+
+	return nil
+
+}
+
+// Get retrieves a field by name.
+func (o *Object) Get(keyName string) Noder {
+	return o.fields[keyName]
+}
+
+// Keys returns a slice of keys in the object.
+func (o *Object) Keys() []Key {
+	var keys []Key
+
+	for _, name := range o.keyList {
+		keys = append(keys, o.keys[name])
+	}
+
+	return keys
+}
+
+// Node converts the object to a jsonnet node.
+func (o *Object) Node() ast.Node {
+	ao := &astext.Object{
+		Oneline: o.Oneline,
+	}
+
+	for _, name := range o.keyList {
+		k := o.keys[name]
+		v := o.fields[name]
+
+		of := astext.ObjectField{
+			Comment: o.generateComment(k.comment),
+		}
+
+		if k.category == ast.ObjectFieldStr {
+			of.Expr1 = NewStringDouble(k.name).Node()
+		} else {
+			of.Id = newIdentifier(k.name)
+		}
+		of.Kind = k.category
+		of.Hide = k.visibility
+		of.Expr2 = v.Node()
+		of.Method = k.Method()
+		of.SuperSugar = k.Mixin()
+
+		ao.Fields = append(ao.Fields, of)
+	}
+
+	return ao
+}
+
+func (o *Object) generateComment(text string) *astext.Comment {
+	if text != "" {
+		return &astext.Comment{Text: text}
+	}
+
+	return nil
+}
+
+// Boolean is a boolean.
+type Boolean struct {
+	value bool
+}
+
+// NewBoolean creates an instance of Boolean.
+func NewBoolean(value bool) *Boolean {
+	return &Boolean{
+		value: value,
+	}
+}
+
+// Node converts Boolean to a jsonnet node.
+func (b *Boolean) Node() ast.Node {
+	return &ast.LiteralBoolean{
+		Value: b.value,
+	}
+}
+
+// StringDouble is double quoted string.
+type StringDouble struct {
+	text string
+}
+
+// NewStringDouble creates an instance of StringDouble.
+func NewStringDouble(text string) *StringDouble {
+	return &StringDouble{
+		text: text,
+	}
+}
+
+func (t *StringDouble) node() *ast.LiteralString {
+	return &ast.LiteralString{
+		Kind:  ast.StringDouble,
+		Value: t.text,
+	}
+}
+
+// Node converts the StringDouble to a jsonnet node.
+func (t *StringDouble) Node() ast.Node {
+	return t.node()
+}
+
+// Number is an a number.
+type Number struct {
+	number float64
+	value  string
+}
+
+var _ Noder = (*Number)(nil)
+
+// NewInt creates an integer number.
+func NewInt(i int) *Number {
+	return &Number{
+		number: float64(i),
+		value:  strconv.Itoa(i),
+	}
+}
+
+// NewFloat creates a float instance of a number.
+func NewFloat(f float64) *Number {
+	return &Number{
+		number: f,
+		value:  strconv.FormatFloat(f, 'f', -1, 64),
+	}
+}
+
+// Node converts the Number to a jsonnet node.
+func (t *Number) Node() ast.Node {
+	return &ast.LiteralNumber{
+		Value:          t.number,
+		OriginalString: t.value,
+	}
+}
+
+// Array is an an array.
+type Array struct {
+	elements []Noder
+}
+
+var _ Noder = (*Array)(nil)
+
+// NewArray creates an instance of Array.
+func NewArray(elements []Noder) *Array {
+	return &Array{
+		elements: elements,
+	}
+}
+
+// Node converts the Array to a jsonnet node.
+func (t *Array) Node() ast.Node {
+	var nodes []ast.Node
+	for _, element := range t.elements {
+		nodes = append(nodes, element.Node())
+	}
+
+	return &ast.Array{
+		Elements: nodes,
+	}
+}
+
+// KeyOptCategory is a functional option for setting key category
+func KeyOptCategory(kc ast.ObjectFieldKind) KeyOpt {
+	return func(k *Key) {
+		k.category = kc
+	}
+}
+
+// KeyOptVisibility is a functional option for setting key visibility
+func KeyOptVisibility(kv ast.ObjectFieldHide) KeyOpt {
+	return func(k *Key) {
+		k.visibility = kv
+	}
+}
+
+// KeyOptComment is a functional option for setting a comment on a key
+func KeyOptComment(text string) KeyOpt {
+	return func(k *Key) {
+		k.comment = text
+	}
+}
+
+// KeyOptMixin is a functional option for setting this key as a mixin
+func KeyOptMixin(b bool) KeyOpt {
+	return func(k *Key) {
+		k.mixin = b
+	}
+}
+
+// KeyOptParams is functional option for setting params for a key. If there are no required
+// parameters, pass an empty []string.
+func KeyOptParams(params []string) KeyOpt {
+	return func(k *Key) {
+		k.params = params
+	}
+}
+
+// KeyOptNamedParams is a functional option for setting named params for a key.
+func KeyOptNamedParams(params ...OptionalArg) KeyOpt {
+	return func(k *Key) {
+		k.namedParams = params
+	}
+}
+
+// KeyOpt is a functional option for configuring Key.
+type KeyOpt func(k *Key)
+
+var (
+	jsonnetReservedWords = []string{"assert", "else", "error", "false", "for", "function", "if",
+		"import", "importstr", "in", "local", "null", "tailstrict", "then", "self", "super", "true"}
+)
+
+// Key names a fields in an object.
+type Key struct {
+	name        string
+	category    ast.ObjectFieldKind
+	visibility  ast.ObjectFieldHide
+	comment     string
+	params      []string
+	namedParams []OptionalArg
+	mixin       bool
+}
+
+var (
+	reStartsWithNonAlpha = regexp.MustCompile(`^[^A-Za-z]`)
+)
+
+// NewKey creates an instance of Key. KeyOpt functional options can be used to configure the
+// newly generated key.
+func NewKey(name string, opts ...KeyOpt) Key {
+
+	category := ast.ObjectFieldID
+	for _, s := range jsonnetReservedWords {
+		if s == name {
+			category = ast.ObjectFieldStr
+		}
+	}
+
+	if reStartsWithNonAlpha.Match([]byte(name)) {
+		category = ast.ObjectFieldStr
+	}
+
+	k := Key{
+		name:     name,
+		category: category,
+	}
+	for _, opt := range opts {
+		opt(&k)
+	}
+
+	return k
+}
+
+// InheritedKey is a convenience method for creating an inherited key.
+func InheritedKey(name string, opts ...KeyOpt) Key {
+	opts = append(opts, KeyOptVisibility(ast.ObjectFieldInherit))
+	return NewKey(name, opts...)
+}
+
+// LocalKey is a convenience method for creating a local key.
+func LocalKey(name string, opts ...KeyOpt) Key {
+	opts = append(opts, KeyOptCategory(ast.ObjectLocal))
+	return NewKey(name, opts...)
+}
+
+// FunctionKey is a convenience method for creating a function key.
+func FunctionKey(name string, args []string, opts ...KeyOpt) Key {
+	opts = append(opts, KeyOptParams(args), KeyOptCategory(ast.ObjectFieldID))
+	return NewKey(name, opts...)
+}
+
+// Method returns the jsonnet AST object file method parameter.
+func (k *Key) Method() *ast.Function {
+	if k.params == nil {
+		return nil
+	}
+
+	f := &ast.Function{
+		Parameters: ast.Parameters{
+			Required: ast.Identifiers{},
+		},
+	}
+
+	for _, p := range k.params {
+		f.Parameters.Required = append(f.Parameters.Required, *newIdentifier(p))
+	}
+
+	for _, p := range k.namedParams {
+		f.Parameters.Optional = append(f.Parameters.Optional, p.NamedParameter())
+	}
+
+	return f
+}
+
+// Mixin returns true if the jsonnet object should be super sugared.
+func (k Key) Mixin() bool {
+	return k.mixin
+}
+
+// BinaryOp is a binary operation.
+type BinaryOp string
+
+const (
+	// BopPlus is +
+	BopPlus BinaryOp = "+"
+	// BopEqual is ==
+	BopEqual = "=="
+	// BopGreater is >
+	BopGreater = ">"
+	// BopAnd is &&
+	BopAnd = "&&"
+)
+
+// Binary represents a binary operation
+type Binary struct {
+	Left  Noder
+	Right Noder
+	Op    BinaryOp
+	Chainer
+}
+
+var _ Noder = (*Binary)(nil)
+
+// NewBinary creates an instance of Binary.
+func NewBinary(left, right Noder, op BinaryOp) *Binary {
+	return &Binary{
+		Left:  left,
+		Right: right,
+		Op:    op,
+	}
+}
+
+// Node converts a BinaryOp into an ast node. This will panic if the binary operator
+// is unknown.
+func (b *Binary) Node() ast.Node {
+	op, ok := ast.BopMap[string(b.Op)]
+	if !ok {
+		panic(fmt.Sprintf("%q is an invalid binary operation", b.Op))
+	}
+
+	return &ast.Binary{
+		Left:  b.Left.Node(),
+		Right: b.Right.Node(),
+		Op:    op,
+	}
+}
+
+// Var represents a variable.
+type Var struct {
+	ID string
+	Chainer
+}
+
+var _ Noder = (*Binary)(nil)
+
+// NewVar creates an instance of Var.
+func NewVar(id string) *Var {
+	return &Var{
+		ID: id,
+	}
+}
+
+// Node converts the var to a jsonnet ast node.
+func (v *Var) Node() ast.Node {
+	return &ast.Var{
+		Id: *newIdentifier(v.ID),
+	}
+}
+
+// Self represents self.
+type Self struct{}
+
+var _ Noder = (*Self)(nil)
+
+// Node converts self to a jsonnet self node.
+func (s *Self) Node() ast.Node {
+	return &ast.Self{}
+}
+
+// Conditional represents a conditional
+type Conditional struct {
+	Cond        Noder
+	BranchTrue  Noder
+	BranchFalse Noder
+	Chainer
+}
+
+var _ Noder = (*Conditional)(nil)
+
+// NewConditional creates an instance of Conditional.
+func NewConditional(cond, tbranch, fbranch Noder) *Conditional {
+	return &Conditional{
+		Cond:        cond,
+		BranchTrue:  tbranch,
+		BranchFalse: fbranch,
+	}
+}
+
+// Node converts the Conditional to a jsonnet ast node.
+func (c *Conditional) Node() ast.Node {
+	cond := &ast.Conditional{
+		Cond:       c.Cond.Node(),
+		BranchTrue: c.BranchTrue.Node(),
+	}
+
+	if c.BranchFalse != nil {
+		cond.BranchFalse = c.BranchFalse.Node()
+	}
+
+	return cond
+}
+
+// OptionalArg is an optional argument.
+type OptionalArg struct {
+	Name    string
+	Default Noder
+}
+
+// NamedArgument converts the OptionalArgument to a jsonnet NamedArgument.
+func (oa *OptionalArg) NamedArgument() ast.NamedArgument {
+	na := ast.NamedArgument{
+		Name: *newIdentifier(oa.Name),
+	}
+
+	if oa.Default == nil {
+		na.Arg = NewStringDouble("").Node()
+	} else {
+		na.Arg = oa.Default.Node()
+	}
+
+	return na
+}
+
+// NamedParameter converts the OptionalArgument to a jsonnet NamedParameter.
+func (oa *OptionalArg) NamedParameter() ast.NamedParameter {
+	np := ast.NamedParameter{
+		Name: *newIdentifier(oa.Name),
+	}
+
+	if oa.Default != nil {
+		np.DefaultArg = oa.Default.Node()
+	}
+
+	return np
+}
+
+// Apply represents an application of a function.
+type Apply struct {
+	target         Chainable
+	positionalArgs []Noder
+	optionalArgs   []OptionalArg
+	Chainer
+}
+
+var _ Targetable = (*Apply)(nil)
+
+// NewApply creates an instance of Apply.
+func NewApply(target Chainable, positionalArgs []Noder, optionalArgs []OptionalArg) *Apply {
+	return &Apply{
+		target:         target,
+		positionalArgs: positionalArgs,
+		optionalArgs:   optionalArgs,
+	}
+}
+
+// ApplyCall creates an Apply using a method string.
+func ApplyCall(method string, args ...Noder) *Apply {
+	return NewApply(NewCall(method), args, nil)
+}
+
+// SetTarget sets the target of this Apply.
+func (a *Apply) SetTarget(c Chainable) {
+	a.target = c
+}
+
+// Node converts the Apply to a jsonnet ast node.
+func (a *Apply) Node() ast.Node {
+	apply := &ast.Apply{
+		Target: a.target.Node(),
+	}
+
+	for _, arg := range a.positionalArgs {
+		apply.Arguments.Positional = append(apply.Arguments.Positional, arg.Node())
+	}
+
+	for _, arg := range a.optionalArgs {
+		apply.Arguments.Named = append(apply.Arguments.Named, arg.NamedArgument())
+	}
+
+	return apply
+}
+
+// Call is a function call.
+type Call struct {
+	parts  []string
+	target Chainable
+	Chainer
+}
+
+var _ Targetable = (*Call)(nil)
+
+// NewCall creates an instance of Call.
+func NewCall(method string) *Call {
+	parts := strings.Split(method, ".")
+
+	return &Call{
+		parts: parts,
+	}
+}
+
+// SetTarget sets the target of this Call.
+func (c *Call) SetTarget(chainable Chainable) {
+	c.target = chainable
+}
+
+// Node converts the Call to a jsonnet ast node.
+func (c *Call) Node() ast.Node {
+	parts := c.parts
+
+	if len(parts) == 1 {
+		return NewVar(parts[0]).Node()
+	}
+
+	var theVar *Var
+	var cur *Index
+
+	switch t := c.target.(type) {
+	case *Var:
+		theVar = t
+	case *Index:
+		cur = t
+	}
+
+	for i := range parts {
+		part := parts[i]
+		if i == 0 && theVar == nil {
+			v := NewVar(part)
+			theVar = v
+			continue
+		}
+		idx := NewIndex(part)
+		if theVar != nil {
+			idx.SetTarget(theVar)
+			theVar = nil
+		} else if cur != nil {
+			idx.SetTarget(cur)
+		}
+
+		cur = idx
+	}
+
+	if theVar != nil {
+		return theVar.Node()
+	}
+
+	return cur.Node()
+}
+
+// Index is an index type.
+type Index struct {
+	ID     string
+	Target Chainable
+	Chainer
+}
+
+var _ Targetable = (*Index)(nil)
+
+// NewIndex creates an instance of Index.
+func NewIndex(id string) *Index {
+	return &Index{
+		ID: id,
+	}
+}
+
+// SetTarget sets the target for this Index.
+func (i *Index) SetTarget(c Chainable) {
+	i.Target = c
+}
+
+// Node converts the Index to a Jsonnet AST node.
+func (i *Index) Node() ast.Node {
+	astIndex := &ast.Index{Id: newIdentifier(i.ID)}
+
+	if i.Target != nil {
+		astIndex.Target = i.Target.Node()
+	}
+
+	return astIndex
+}
+
+// Chainable is an interface that signifies this object can be
+// used in CallChain.
+type Chainable interface {
+	Chainable()
+	Node() ast.Node
+}
+
+// Targetable is a Chainable that allows you to set a target.
+// Can be used with Calls, Indexes, and Applies.
+type Targetable interface {
+	Chainable
+	SetTarget(Chainable)
+}
+
+// Chainer is an extension struct to bring the Chainable
+// function into a type.
+type Chainer struct{}
+
+// Chainable implements the Chainable interface.
+func (c *Chainer) Chainable() {}
+
+// CallChain creates a call chain. It allows you to string
+// an arbitrary amount of Chainables together.
+type CallChain struct {
+	links []Chainable
+}
+
+var _ Noder = (*CallChain)(nil)
+
+// NewCallChain creates an instance of CallChain.
+func NewCallChain(links ...Chainable) *CallChain {
+
+	return &CallChain{
+		links: links,
+	}
+}
+
+// Node converts the CallChain to a Jsonnet AST node.
+func (cc *CallChain) Node() ast.Node {
+	if len(cc.links) == 1 {
+		return cc.links[0].Node()
+	}
+
+	var previous Chainable
+
+	for i := range cc.links {
+		switch t := cc.links[i].(type) {
+		default:
+			panic(fmt.Sprintf("unhandled node type %T", t))
+		case *Var:
+			previous = t
+		case *Index:
+			if previous != nil {
+				t.SetTarget(previous)
+			}
+
+			previous = t
+		case *Apply:
+			if previous != nil {
+				if targetable, ok := t.target.(Targetable); ok {
+					targetable.SetTarget(previous)
+				}
+			}
+
+			previous = t
+		case *Call:
+			if previous != nil {
+				t.SetTarget(previous)
+			}
+
+			previous = t
+		}
+	}
+
+	return previous.Node()
+}
+
+// Local is a local declaration.
+type Local struct {
+	name  string
+	value Noder
+	Body  Noder
+}
+
+var _ Noder = (*Local)(nil)
+
+// NewLocal creates an instance of Local.
+func NewLocal(name string, value, body Noder) *Local {
+	return &Local{name: name, value: value, Body: body}
+}
+
+// Node converts the Local to a jsonnet ast node.
+func (l *Local) Node() ast.Node {
+	id := *newIdentifier(l.name)
+
+	local := &ast.Local{
+		Binds: ast.LocalBinds{
+			{
+				Variable: id,
+				Body:     l.value.Node(),
+			},
+		},
+	}
+
+	if l.Body != nil {
+		local.Body = l.Body.Node()
+	}
+
+	return local
+}
+
+// Import is an import declaration.
+type Import struct {
+	name string
+}
+
+var _ Noder = (*Import)(nil)
+
+// NewImport creates an instance of Import.
+func NewImport(name string) *Import {
+	return &Import{name: name}
+}
+
+// Node converts the Import to a jsonnet ast node.
+func (i *Import) Node() ast.Node {
+	file := NewStringDouble(i.name)
+
+	return &ast.Import{
+		File: file.node(),
+	}
+}
+
+// Function is a function.
+type Function struct {
+	req  []string
+	body Noder
+}
+
+var _ Noder = (*Function)(nil)
+
+// NewFunction creates an instance of Function.
+func NewFunction(req []string, body Noder) *Function {
+	return &Function{
+		req:  req,
+		body: body,
+	}
+}
+
+// Node converts the Function to a jsonnet ast node.
+func (f *Function) Node() ast.Node {
+	fun := &ast.Function{
+		Parameters: ast.Parameters{},
+		Body:       f.body.Node(),
+	}
+
+	var ids ast.Identifiers
+	for _, param := range f.req {
+		ids = append(ids, *newIdentifier(param))
+	}
+	fun.Parameters.Required = ids
+
+	return fun
+}
+
+// Combine combines multiple nodes into a single node. If one argument is passed,
+// it is returned. If two or more arguments are passed, they are combined using a
+// Binary.
+func Combine(nodes ...Noder) Noder {
+	l := len(nodes)
+
+	switch {
+	case l == 1:
+		return nodes[0]
+	case l >= 2:
+		sum := NewBinary(nodes[0], nodes[1], BopPlus)
+
+		for i := 2; i < l; i++ {
+			sum = NewBinary(sum, nodes[i], BopPlus)
+		}
+
+		return sum
+	}
+
+	return NewObject()
+}
+
+// newIdentifier creates an identifier.
+func newIdentifier(value string) *ast.Identifier {
+	id := ast.Identifier(value)
+	return &id
+}
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/printer/doc.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/printer/doc.go
new file mode 100644
index 0000000000000000000000000000000000000000..8053704fcaa480403a5ac66f12deeb47f20e41c6
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/printer/doc.go
@@ -0,0 +1,2 @@
+// Package printer implements printing of jsonnet AST nodes.
+package printer
diff --git a/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/printer/printer.go b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/printer/printer.go
new file mode 100644
index 0000000000000000000000000000000000000000..d2a58842628cbc7d655741bf629f2e033d55d49e
--- /dev/null
+++ b/vendor/github.com/ksonnet/ksonnet-lib/ksonnet-gen/printer/printer.go
@@ -0,0 +1,529 @@
+package printer
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"strconv"
+	"strings"
+
+	"github.com/google/go-jsonnet/ast"
+	"github.com/ksonnet/ksonnet-lib/ksonnet-gen/astext"
+	"github.com/pkg/errors"
+)
+
+const (
+	space   = byte(' ')
+	tab     = byte('\t')
+	newline = byte('\n')
+	comma   = byte(',')
+
+	syntaxSugar = '+'
+)
+
+// Fprint prints a node to the supplied writer using the default
+// configuration.
+func Fprint(output io.Writer, node ast.Node) error {
+	return DefaultConfig.Fprint(output, node)
+}
+
+// DefaultConfig is a default configuration.
+var DefaultConfig = Config{
+	IndentSize: 2,
+}
+
+// IndentMode is the indent mode for Config.
+type IndentMode int
+
+const (
+	// IndentModeSpace indents with spaces.
+	IndentModeSpace IndentMode = iota
+	// IndentModeTab indents with tabs.
+	IndentModeTab
+)
+
+// Config is a configuration for the printer.
+type Config struct {
+	IndentSize int
+	IndentMode IndentMode
+}
+
+// Fprint prints a node to the supplied writer.
+func (c *Config) Fprint(output io.Writer, node ast.Node) error {
+	p := printer{cfg: *c}
+
+	p.print(node)
+
+	if p.err != nil {
+		return errors.Wrap(p.err, "output")
+	}
+
+	_, err := output.Write(p.output)
+	return err
+}
+
+type printer struct {
+	cfg Config
+
+	output      []byte
+	indentLevel int
+	inFunction  bool
+
+	err error
+}
+
+func (p *printer) indent() {
+	if len(p.output) == 0 {
+		return
+	}
+
+	r := p.indentLevel
+	var ch byte
+	if p.cfg.IndentMode == IndentModeTab {
+		ch = tab
+	} else {
+		ch = space
+		r = r * p.cfg.IndentSize
+	}
+
+	last := p.output[len(p.output)-1]
+	if last == newline {
+		pre := bytes.Repeat([]byte{ch}, r)
+		p.output = append(p.output, pre...)
+	}
+}
+
+func (p *printer) writeByte(ch byte, n int) {
+	if p.err != nil {
+		return
+	}
+
+	for i := 0; i < n; i++ {
+		p.output = append(p.output, ch)
+	}
+
+	p.indent()
+}
+
+func (p *printer) writeString(s string) {
+	for _, b := range []byte(s) {
+		p.writeByte(b, 1)
+	}
+}
+
+// printer prints a node.
+// nolint: gocyclo
+func (p *printer) print(n interface{}) {
+	if p.err != nil {
+		return
+	}
+
+	if n == nil {
+		p.err = errors.New("node is nil")
+		return
+	}
+
+	switch t := n.(type) {
+	default:
+		p.err = errors.Errorf("unknown node type: (%T) %v", n, n)
+		return
+	case *ast.Apply:
+		p.handleApply(t)
+	case ast.Arguments:
+		p.handleArguments(t)
+	case *ast.Array:
+		p.writeString("[")
+		for i := 0; i < len(t.Elements); i++ {
+			p.print(t.Elements[i])
+
+			if i < len(t.Elements)-1 {
+				p.writeString(",")
+			}
+		}
+		p.writeString("]")
+	case *ast.Binary:
+		p.print(t.Left)
+		p.writeByte(space, 1)
+
+		p.writeString(t.Op.String())
+		p.writeByte(space, 1)
+
+		p.print(t.Right)
+	case *ast.Conditional:
+		p.handleConditional(t)
+	case *ast.Function:
+		p.addMethodSignature(t)
+	case *ast.Import:
+		p.writeString("import ")
+		p.print(t.File)
+	case *ast.Index:
+		p.handleIndex(t)
+	case *ast.Local:
+		p.handleLocal(t)
+	case *ast.Object:
+		p.writeString("{")
+
+		for _, field := range t.Fields {
+			if !p.inFunction {
+				p.indentLevel++
+				p.writeByte(newline, 1)
+			}
+
+			p.print(field)
+
+			if !p.inFunction {
+				p.indentLevel--
+				p.writeByte(comma, 1)
+			}
+		}
+
+		// write an extra newline at the end
+		if !p.inFunction {
+			p.writeByte(newline, 1)
+		}
+
+		p.writeString("}")
+	case *astext.Object:
+		p.writeString("{")
+
+		for i, field := range t.Fields {
+			if !t.Oneline && !p.inFunction {
+				p.indentLevel++
+				p.writeByte(newline, 1)
+			}
+
+			p.print(field)
+			if i < len(t.Fields)-1 {
+				if t.Oneline {
+					p.writeByte(comma, 1)
+					p.writeByte(space, 1)
+				}
+			}
+
+			if !t.Oneline && !p.inFunction {
+				p.indentLevel--
+				p.writeByte(comma, 1)
+			}
+		}
+
+		// write an extra newline at the end
+		if !t.Oneline && !p.inFunction {
+			p.writeByte(newline, 1)
+		}
+
+		p.writeString("}")
+	case astext.ObjectField, ast.ObjectField:
+		p.handleObjectField(t)
+	case *ast.LiteralBoolean:
+		if t.Value {
+			p.writeString("true")
+		} else {
+			p.writeString("false")
+		}
+	case *ast.LiteralString:
+		switch t.Kind {
+		default:
+			p.err = errors.Errorf("unknown string literal kind %#v", t.Kind)
+			return
+		case ast.StringDouble:
+			p.writeString(strconv.Quote(t.Value))
+		case ast.StringSingle:
+			p.writeString(fmt.Sprintf("'%s'", t.Value))
+		}
+
+	case *ast.LiteralNumber:
+		p.writeString(t.OriginalString)
+	case *ast.Self:
+		p.writeString("self")
+	case *ast.Var:
+		p.writeString(string(t.Id))
+	}
+}
+
+func (p *printer) handleApply(a *ast.Apply) {
+	switch a.Target.(type) {
+	default:
+		p.writeString("function")
+		p.writeString("(")
+		p.print(a.Arguments)
+		p.writeString(")")
+		p.writeByte(space, 1)
+		p.print(a.Target)
+	case *ast.Apply, *ast.Index, *ast.Self, *ast.Var:
+		p.print(a.Target)
+		p.writeString("(")
+		p.print(a.Arguments)
+		p.writeString(")")
+	}
+}
+
+func (p *printer) handleArguments(a ast.Arguments) {
+	// NOTE: only supporting positional arguments
+	for i, arg := range a.Positional {
+		p.print(arg)
+		if i < len(a.Positional)-1 {
+			p.writeByte(comma, 1)
+			p.writeByte(space, 1)
+		}
+	}
+}
+
+func (p *printer) handleConditional(c *ast.Conditional) {
+	p.writeString("if ")
+	p.print(c.Cond)
+
+	p.writeString(" then ")
+	p.print(c.BranchTrue)
+
+	if c.BranchFalse != nil {
+		p.writeString(" else ")
+		p.print(c.BranchFalse)
+	}
+}
+
+func (p *printer) writeComment(c *astext.Comment) {
+	if c == nil {
+		return
+	}
+
+	lines := strings.Split(c.Text, "\n")
+	for _, line := range lines {
+		p.writeString("//")
+		if len(line) > 0 {
+			p.writeByte(space, 1)
+		}
+		p.writeString(strings.TrimSpace(line))
+		p.writeByte(newline, 1)
+	}
+}
+
+func (p *printer) handleIndex(i *ast.Index) {
+	if i == nil {
+		p.err = errors.New("index is nil")
+		return
+	}
+	p.print(i.Target)
+	p.writeString(".")
+
+	id, err := indexID(i)
+	if err != nil {
+		p.err = err
+		return
+	}
+	p.writeString(id)
+
+}
+
+func (p *printer) handleLocal(l *ast.Local) {
+	p.writeString("local ")
+
+	for _, bind := range l.Binds {
+		p.writeString(string(bind.Variable))
+		switch bodyType := bind.Body.(type) {
+		default:
+			p.writeString(" = ")
+			p.print(bind.Body)
+			p.writeString(";")
+		case *ast.Function:
+			p.print(bind.Body)
+			p.handleLocalFunction(bodyType)
+		}
+		c := 1
+		if _, ok := l.Body.(*ast.Local); !ok {
+			c = 2
+		}
+		p.writeByte(newline, c)
+
+	}
+
+	p.print(l.Body)
+}
+
+func (p *printer) handleLocalFunction(f *ast.Function) {
+	p.writeString(" =")
+	switch f.Body.(type) {
+	default:
+		p.writeByte(space, 1)
+		p.print(f.Body)
+		p.writeString(";")
+	case *ast.Local:
+		p.indentLevel++
+		p.writeByte(newline, 1)
+		p.print(f.Body)
+		p.writeString(";")
+		p.indentLevel--
+	}
+}
+
+func fieldID(expr1 ast.Node, id *ast.Identifier) string {
+	if expr1 != nil {
+		ls := expr1.(*ast.LiteralString)
+		return fmt.Sprintf(`"%s"`, ls.Value)
+	}
+
+	if id != nil {
+		return string(*id)
+	}
+
+	return ""
+}
+
+func (p *printer) handleObjectField(n interface{}) {
+	var ofHide ast.ObjectFieldHide
+	var ofKind ast.ObjectFieldKind
+	var ofID string
+	var ofMethod *ast.Function
+	var ofSugar bool
+	var ofExpr2 ast.Node
+
+	switch t := n.(type) {
+	default:
+		p.err = errors.Errorf("unknown object field type %T", t)
+		return
+	case ast.ObjectField:
+		ofHide = t.Hide
+		ofKind = t.Kind
+		ofID = fieldID(t.Expr1, t.Id)
+		ofMethod = t.Method
+		ofSugar = t.SuperSugar
+		ofExpr2 = t.Expr2
+	case astext.ObjectField:
+		ofHide = t.Hide
+		ofKind = t.Kind
+		ofID = fieldID(t.Expr1, t.Id)
+		ofMethod = t.Method
+		ofSugar = t.SuperSugar
+		ofExpr2 = t.Expr2
+		p.writeComment(t.Comment)
+	}
+
+	if ofID == "" {
+		p.err = errors.New("id is not defined")
+		return
+	}
+
+	var fieldType string
+
+	switch ofHide {
+	default:
+		p.err = errors.Errorf("unknown Hide type %#v", ofHide)
+		return
+	case ast.ObjectFieldHidden:
+		fieldType = "::"
+	case ast.ObjectFieldVisible:
+		fieldType = ":::"
+	case ast.ObjectFieldInherit:
+		fieldType = ":"
+	}
+
+	switch ofKind {
+	default:
+		p.err = errors.Errorf("unknown Kind type %#v", ofKind)
+		return
+	case ast.ObjectFieldID:
+		p.writeString(ofID)
+		if ofMethod != nil {
+			p.addMethodSignature(ofMethod)
+		}
+
+		if ofSugar {
+			p.writeByte(syntaxSugar, 1)
+		}
+
+		p.writeString(fieldType)
+
+		if isLocal(ofExpr2) {
+			p.indentLevel++
+			p.writeByte(newline, 1)
+			p.print(ofExpr2)
+			p.indentLevel--
+
+		} else {
+			p.writeByte(space, 1)
+			p.print(ofExpr2)
+		}
+
+	case ast.ObjectLocal:
+		p.writeString("local ")
+		p.writeString(ofID)
+		p.addMethodSignature(ofMethod)
+		p.writeString(" = ")
+		p.print(ofExpr2)
+	case ast.ObjectFieldStr:
+		p.writeString(fmt.Sprintf(`%s%s `, ofID, fieldType))
+		p.print(ofExpr2)
+	}
+}
+
+func isLocal(node ast.Node) bool {
+	switch node.(type) {
+	default:
+		return false
+	case *ast.Local:
+		return true
+	}
+}
+
+func (p *printer) addMethodSignature(method *ast.Function) {
+	if method == nil {
+		return
+	}
+	params := method.Parameters
+
+	p.writeString("(")
+	var args []string
+	for _, arg := range params.Required {
+		args = append(args, string(arg))
+	}
+
+	for _, opt := range params.Optional {
+		if opt.DefaultArg == nil {
+			continue
+		}
+		var arg string
+		arg += string(opt.Name)
+		arg += "="
+
+		child := printer{cfg: p.cfg}
+		child.inFunction = true
+		child.print(opt.DefaultArg)
+		if child.err != nil {
+			p.err = errors.Wrapf(child.err, "invalid argument for %s", string(opt.Name))
+			return
+		}
+
+		arg += string(child.output)
+
+		args = append(args, arg)
+	}
+
+	p.writeString(strings.Join(args, ", "))
+	p.writeString(")")
+}
+
+func literalStringValue(ls *ast.LiteralString) (string, error) {
+	if ls == nil {
+		return "", errors.New("literal string is nil")
+	}
+
+	return ls.Value, nil
+}
+
+func indexID(i *ast.Index) (string, error) {
+	if i == nil {
+		return "", errors.New("index is nil")
+	}
+
+	if i.Index != nil {
+		ls, ok := i.Index.(*ast.LiteralString)
+		if !ok {
+			return "", errors.New("index is not a literal string")
+		}
+
+		return literalStringValue(ls)
+	} else if i.Id != nil {
+		return string(*i.Id), nil
+	} else {
+		return "", errors.New("index and id can't both be blank")
+	}
+}