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 = ¶m + } + } + + 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") + } +}