mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 02:08:13 +00:00 
			
		
		
		
	Add lister-gen
This commit is contained in:
		
							
								
								
									
										1
									
								
								cmd/libs/go2idl/lister-gen/.import-restrictions
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								cmd/libs/go2idl/lister-gen/.import-restrictions
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| {} | ||||
							
								
								
									
										345
									
								
								cmd/libs/go2idl/lister-gen/generators/lister.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										345
									
								
								cmd/libs/go2idl/lister-gen/generators/lister.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,345 @@ | ||||
| /* | ||||
| Copyright 2016 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package generators | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
|  | ||||
| 	"k8s.io/gengo/args" | ||||
| 	"k8s.io/gengo/generator" | ||||
| 	"k8s.io/gengo/namer" | ||||
| 	"k8s.io/gengo/types" | ||||
| 	clientgentypes "k8s.io/kubernetes/cmd/libs/go2idl/client-gen/types" | ||||
|  | ||||
| 	"github.com/golang/glog" | ||||
| 	"github.com/spf13/pflag" | ||||
| ) | ||||
|  | ||||
| // NameSystems returns the name system used by the generators in this package. | ||||
| func NameSystems() namer.NameSystems { | ||||
| 	pluralExceptions := map[string]string{ | ||||
| 		"Endpoints": "Endpoints", | ||||
| 	} | ||||
| 	return namer.NameSystems{ | ||||
| 		"public":             namer.NewPublicNamer(0), | ||||
| 		"private":            namer.NewPrivateNamer(0), | ||||
| 		"raw":                namer.NewRawNamer("", nil), | ||||
| 		"publicPlural":       namer.NewPublicPluralNamer(pluralExceptions), | ||||
| 		"allLowercasePlural": namer.NewAllLowercasePluralNamer(pluralExceptions), | ||||
| 		"lowercaseSingular":  &lowercaseSingularNamer{}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // lowercaseSingularNamer implements Namer | ||||
| type lowercaseSingularNamer struct{} | ||||
|  | ||||
| // Name returns t's name in all lowercase. | ||||
| func (n *lowercaseSingularNamer) Name(t *types.Type) string { | ||||
| 	return strings.ToLower(t.Name.Name) | ||||
| } | ||||
|  | ||||
| // DefaultNameSystem returns the default name system for ordering the types to be | ||||
| // processed by the generators in this package. | ||||
| func DefaultNameSystem() string { | ||||
| 	return "public" | ||||
| } | ||||
|  | ||||
| // generatedBy returns information about the arguments used to invoke | ||||
| // lister-gen. | ||||
| func generatedBy() string { | ||||
| 	var cmdArgs string | ||||
| 	pflag.VisitAll(func(f *pflag.Flag) { | ||||
| 		if !f.Changed || f.Name == "verify-only" { | ||||
| 			return | ||||
| 		} | ||||
| 		cmdArgs += fmt.Sprintf("--%s=%s ", f.Name, f.Value) | ||||
| 	}) | ||||
| 	return fmt.Sprintf("\n// This file was automatically generated by lister-gen with arguments: %s\n\n", cmdArgs) | ||||
| } | ||||
|  | ||||
| // Packages makes the client package definition. | ||||
| func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages { | ||||
| 	boilerplate, err := arguments.LoadGoBoilerplate() | ||||
| 	if err != nil { | ||||
| 		glog.Fatalf("Failed loading boilerplate: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	boilerplate = append(boilerplate, []byte(generatedBy())...) | ||||
|  | ||||
| 	var packageList generator.Packages | ||||
| 	for _, inputDir := range arguments.InputDirs { | ||||
| 		p := context.Universe.Package(inputDir) | ||||
|  | ||||
| 		objectMeta, err := objectMetaForPackage(p) | ||||
| 		if err != nil { | ||||
| 			glog.Fatal(err) | ||||
| 		} | ||||
| 		if objectMeta == nil { | ||||
| 			// no types in this package had genclient | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		var gv clientgentypes.GroupVersion | ||||
|  | ||||
| 		if isInternal(objectMeta) { | ||||
| 			lastSlash := strings.LastIndex(p.Path, "/") | ||||
| 			if lastSlash == -1 { | ||||
| 				glog.Fatalf("error constructing internal group version for package %q", p.Path) | ||||
| 			} | ||||
| 			gv.Group = clientgentypes.Group(p.Path[lastSlash+1:]) | ||||
| 		} else { | ||||
| 			parts := strings.Split(p.Path, "/") | ||||
| 			gv.Group = clientgentypes.Group(parts[len(parts)-2]) | ||||
| 			gv.Version = clientgentypes.Version(parts[len(parts)-1]) | ||||
| 		} | ||||
|  | ||||
| 		packageList = append(packageList, &generator.DefaultPackage{ | ||||
| 			PackageName: strings.ToLower(gv.Version.NonEmpty()), | ||||
| 			PackagePath: filepath.Join(arguments.OutputPackagePath, strings.ToLower(gv.Group.NonEmpty()), strings.ToLower(gv.Version.NonEmpty())), | ||||
| 			HeaderText:  boilerplate, | ||||
| 			GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { | ||||
| 				for _, t := range p.Types { | ||||
| 					// filter out types which dont have genclient=true. | ||||
| 					if extractBoolTagOrDie("genclient", t.SecondClosestCommentLines) == false { | ||||
| 						continue | ||||
| 					} | ||||
| 					generators = append(generators, &listerGenerator{ | ||||
| 						DefaultGen: generator.DefaultGen{ | ||||
| 							OptionalName: arguments.OutputFileBaseName + "." + strings.ToLower(t.Name.Name), | ||||
| 						}, | ||||
| 						outputPackage:  arguments.OutputPackagePath, | ||||
| 						groupVersion:   gv, | ||||
| 						typeToGenerate: t, | ||||
| 						imports:        generator.NewImportTracker(), | ||||
| 						objectMeta:     objectMeta, | ||||
| 					}) | ||||
| 				} | ||||
| 				return generators | ||||
| 			}, | ||||
| 			FilterFunc: func(c *generator.Context, t *types.Type) bool { | ||||
| 				// piggy-back on types that are tagged for client-gen | ||||
| 				return extractBoolTagOrDie("genclient", t.SecondClosestCommentLines) == true | ||||
| 			}, | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	return packageList | ||||
| } | ||||
|  | ||||
| // objectMetaForPackage returns the type of ObjectMeta used by package p. | ||||
| func objectMetaForPackage(p *types.Package) (*types.Type, error) { | ||||
| 	generatingForPackage := false | ||||
| 	for _, t := range p.Types { | ||||
| 		// filter out types which dont have genclient=true. | ||||
| 		if extractBoolTagOrDie("genclient", t.SecondClosestCommentLines) == false { | ||||
| 			continue | ||||
| 		} | ||||
| 		generatingForPackage = true | ||||
| 		for _, member := range t.Members { | ||||
| 			if member.Name == "ObjectMeta" { | ||||
| 				return member.Type, nil | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if generatingForPackage { | ||||
| 		return nil, fmt.Errorf("unable to find ObjectMeta for any types in package %s", p.Path) | ||||
| 	} | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| // isInternal returns true if t's package is k8s.io/kubernetes/pkg/api. | ||||
| func isInternal(t *types.Type) bool { | ||||
| 	return t.Name.Package == "k8s.io/kubernetes/pkg/api" | ||||
| } | ||||
|  | ||||
| // listerGenerator produces a file of listers for a given GroupVersion and | ||||
| // type. | ||||
| type listerGenerator struct { | ||||
| 	generator.DefaultGen | ||||
| 	outputPackage  string | ||||
| 	groupVersion   clientgentypes.GroupVersion | ||||
| 	typeToGenerate *types.Type | ||||
| 	imports        namer.ImportTracker | ||||
| 	objectMeta     *types.Type | ||||
| } | ||||
|  | ||||
| var _ generator.Generator = &listerGenerator{} | ||||
|  | ||||
| func (g *listerGenerator) Filter(c *generator.Context, t *types.Type) bool { | ||||
| 	return t == g.typeToGenerate | ||||
| } | ||||
|  | ||||
| func (g *listerGenerator) Namers(c *generator.Context) namer.NameSystems { | ||||
| 	return namer.NameSystems{ | ||||
| 		"raw": namer.NewRawNamer(g.outputPackage, g.imports), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (g *listerGenerator) Imports(c *generator.Context) (imports []string) { | ||||
| 	imports = append(imports, g.imports.ImportLines()...) | ||||
| 	imports = append(imports, "k8s.io/kubernetes/pkg/api/errors") | ||||
| 	imports = append(imports, "k8s.io/kubernetes/pkg/labels") | ||||
| 	// for Indexer | ||||
| 	imports = append(imports, "k8s.io/kubernetes/pkg/client/cache") | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (g *listerGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { | ||||
| 	sw := generator.NewSnippetWriter(w, c, "$", "$") | ||||
|  | ||||
| 	glog.V(5).Infof("processing type %v", t) | ||||
| 	m := map[string]interface{}{ | ||||
| 		"group":      g.groupVersion.Group.String(), | ||||
| 		"type":       t, | ||||
| 		"objectMeta": g.objectMeta, | ||||
| 	} | ||||
|  | ||||
| 	namespaced := !extractBoolTagOrDie("nonNamespaced", t.SecondClosestCommentLines) | ||||
| 	if namespaced { | ||||
| 		sw.Do(typeListerInterface, m) | ||||
| 	} else { | ||||
| 		sw.Do(typeListerInterface_NonNamespaced, m) | ||||
| 	} | ||||
|  | ||||
| 	sw.Do(typeListerStruct, m) | ||||
| 	sw.Do(typeListerConstructor, m) | ||||
| 	sw.Do(typeLister_List, m) | ||||
|  | ||||
| 	if namespaced { | ||||
| 		sw.Do(typeLister_NamespaceLister, m) | ||||
| 		sw.Do(namespaceListerInterface, m) | ||||
| 		sw.Do(namespaceListerStruct, m) | ||||
| 		sw.Do(namespaceLister_List, m) | ||||
| 		sw.Do(namespaceLister_Get, m) | ||||
| 	} else { | ||||
| 		sw.Do(typeLister_NonNamespacedGet, m) | ||||
| 	} | ||||
|  | ||||
| 	return sw.Error() | ||||
| } | ||||
|  | ||||
| var typeListerInterface = ` | ||||
| // $.type|public$Lister helps list $.type|publicPlural$. | ||||
| type $.type|public$Lister interface { | ||||
| 	// List lists all $.type|publicPlural$ in the indexer. | ||||
| 	List(selector labels.Selector) (ret []*$.type|raw$, err error) | ||||
| 	// $.type|publicPlural$ returns an object that can list and get $.type|publicPlural$. | ||||
| 	$.type|publicPlural$(namespace string) $.type|public$NamespaceLister | ||||
| } | ||||
| ` | ||||
|  | ||||
| var typeListerInterface_NonNamespaced = ` | ||||
| // $.type|public$Lister helps list $.type|publicPlural$. | ||||
| type $.type|public$Lister interface { | ||||
| 	// List lists all $.type|publicPlural$ in the indexer. | ||||
| 	List(selector labels.Selector) (ret []*$.type|raw$, err error) | ||||
| 	// Get retrieves the $.type|public$ from the index for a given name. | ||||
| 	Get(name string) (*$.type|raw$, error) | ||||
| } | ||||
| ` | ||||
|  | ||||
| var typeListerStruct = ` | ||||
| // $.type|private$Lister implements the $.type|public$Lister interface. | ||||
| type $.type|private$Lister struct { | ||||
| 	indexer cache.Indexer | ||||
| } | ||||
| ` | ||||
|  | ||||
| var typeListerConstructor = ` | ||||
| // New$.type|public$Lister returns a new $.type|public$Lister. | ||||
| func New$.type|public$Lister(indexer cache.Indexer) $.type|public$Lister { | ||||
| 	return &$.type|private$Lister{indexer: indexer} | ||||
| } | ||||
| ` | ||||
|  | ||||
| var typeLister_List = ` | ||||
| // List lists all $.type|publicPlural$ in the indexer. | ||||
| func (s *$.type|private$Lister) List(selector labels.Selector) (ret []*$.type|raw$, err error) { | ||||
| 	err = cache.ListAll(s.indexer, selector, func(m interface{}) { | ||||
| 		ret = append(ret, m.(*$.type|raw$)) | ||||
| 	}) | ||||
| 	return ret, err | ||||
| } | ||||
| ` | ||||
|  | ||||
| var typeLister_NamespaceLister = ` | ||||
| // $.type|publicPlural$ returns an object that can list and get $.type|publicPlural$. | ||||
| func (s *$.type|private$Lister) $.type|publicPlural$(namespace string) $.type|public$NamespaceLister { | ||||
| 	return $.type|private$NamespaceLister{indexer: s.indexer, namespace: namespace} | ||||
| } | ||||
| ` | ||||
|  | ||||
| var typeLister_NonNamespacedGet = ` | ||||
| // Get retrieves the $.type|public$ from the index for a given name. | ||||
| func (s *$.type|private$Lister) Get(name string) (*$.type|raw$, error) { | ||||
|   key := &$.type|raw${ObjectMeta: $.objectMeta|raw${Name: name}} | ||||
|   obj, exists, err := s.indexer.Get(key) | ||||
|   if err != nil { | ||||
|     return nil, err | ||||
|   } | ||||
|   if !exists { | ||||
|     return nil, errors.NewNotFound($.group$.Resource("$.type|lowercaseSingular$"), name) | ||||
|   } | ||||
|   return obj.(*$.type|raw$), nil | ||||
| } | ||||
| ` | ||||
|  | ||||
| var namespaceListerInterface = ` | ||||
| // $.type|public$NamespaceLister helps list and get $.type|publicPlural$. | ||||
| type $.type|public$NamespaceLister interface { | ||||
| 	// List lists all $.type|publicPlural$ in the indexer for a given namespace. | ||||
| 	List(selector labels.Selector) (ret []*$.type|raw$, err error) | ||||
| 	// Get retrieves the $.type|public$ from the indexer for a given namespace and name. | ||||
| 	Get(name string) (*$.type|raw$, error) | ||||
| } | ||||
| ` | ||||
|  | ||||
| var namespaceListerStruct = ` | ||||
| // $.type|private$NamespaceLister implements the $.type|public$NamespaceLister | ||||
| // interface. | ||||
| type $.type|private$NamespaceLister struct { | ||||
| 	indexer cache.Indexer | ||||
| 	namespace string | ||||
| } | ||||
| ` | ||||
|  | ||||
| var namespaceLister_List = ` | ||||
| // List lists all $.type|publicPlural$ in the indexer for a given namespace. | ||||
| func (s $.type|private$NamespaceLister) List(selector labels.Selector) (ret []*$.type|raw$, err error) { | ||||
| 	err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { | ||||
| 		ret = append(ret, m.(*$.type|raw$)) | ||||
| 	}) | ||||
| 	return ret, err | ||||
| } | ||||
| ` | ||||
|  | ||||
| var namespaceLister_Get = ` | ||||
| // Get retrieves the $.type|public$ from the indexer for a given namespace and name. | ||||
| func (s $.type|private$NamespaceLister) Get(name string) (*$.type|raw$, error) { | ||||
| 	obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if !exists { | ||||
| 		return nil, errors.NewNotFound($.group$.Resource("$.type|lowercaseSingular$"), name) | ||||
| 	} | ||||
| 	return obj.(*$.type|raw$), nil | ||||
| } | ||||
| ` | ||||
							
								
								
									
										33
									
								
								cmd/libs/go2idl/lister-gen/generators/tags.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								cmd/libs/go2idl/lister-gen/generators/tags.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| /* | ||||
| Copyright 2016 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package generators | ||||
|  | ||||
| import ( | ||||
| 	"github.com/golang/glog" | ||||
| 	"k8s.io/gengo/types" | ||||
| ) | ||||
|  | ||||
| // extractBoolTagOrDie gets the comment-tags for the key and asserts that, if | ||||
| // it exists, the value is boolean.  If the tag did not exist, it returns | ||||
| // false. | ||||
| func extractBoolTagOrDie(key string, lines []string) bool { | ||||
| 	val, err := types.ExtractSingleBoolCommentTag("+", key, false, lines) | ||||
| 	if err != nil { | ||||
| 		glog.Fatalf(err.Error()) | ||||
| 	} | ||||
| 	return val | ||||
| } | ||||
							
								
								
									
										48
									
								
								cmd/libs/go2idl/lister-gen/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								cmd/libs/go2idl/lister-gen/main.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| /* | ||||
| Copyright 2016 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	"k8s.io/gengo/args" | ||||
| 	"k8s.io/kubernetes/cmd/libs/go2idl/lister-gen/generators" | ||||
|  | ||||
| 	"github.com/golang/glog" | ||||
| 	"github.com/spf13/pflag" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	arguments := &args.GeneratorArgs{ | ||||
| 		OutputBase:         args.DefaultSourceTree(), | ||||
| 		GoHeaderFilePath:   filepath.Join(args.DefaultSourceTree(), "k8s.io/kubernetes/hack/boilerplate/boilerplate.go.txt"), | ||||
| 		GeneratedBuildTag:  "ignore_autogenerated", | ||||
| 		OutputFileBaseName: "zz_generated", | ||||
| 		OutputPackagePath:  "k8s.io/kubernetes/pkg/client/listers", | ||||
| 	} | ||||
| 	arguments.AddFlags(pflag.CommandLine) | ||||
|  | ||||
| 	// Run it. | ||||
| 	if err := arguments.Execute( | ||||
| 		generators.NameSystems(), | ||||
| 		generators.DefaultNameSystem(), | ||||
| 		generators.Packages, | ||||
| 	); err != nil { | ||||
| 		glog.Fatalf("Error: %v", err) | ||||
| 	} | ||||
| 	glog.V(2).Info("Completed successfully.") | ||||
| } | ||||
| @@ -33,6 +33,7 @@ cmd/libs/go2idl/generator | ||||
| cmd/libs/go2idl/go-to-protobuf | ||||
| cmd/libs/go2idl/go-to-protobuf/protoc-gen-gogo | ||||
| cmd/libs/go2idl/import-boss | ||||
| cmd/libs/go2idl/lister-gen | ||||
| cmd/libs/go2idl/openapi-gen | ||||
| cmd/libs/go2idl/parser | ||||
| cmd/libs/go2idl/set-gen | ||||
|   | ||||
| @@ -26,11 +26,13 @@ kube::golang::setup_env | ||||
| BUILD_TARGETS=( | ||||
|   cmd/libs/go2idl/client-gen | ||||
|   cmd/libs/go2idl/set-gen | ||||
|   cmd/libs/go2idl/lister-gen | ||||
| ) | ||||
| make -C "${KUBE_ROOT}" WHAT="${BUILD_TARGETS[*]}" | ||||
|  | ||||
| clientgen=$(kube::util::find-binary "client-gen") | ||||
| setgen=$(kube::util::find-binary "set-gen") | ||||
| listergen=$(kube::util::find-binary "lister-gen") | ||||
|  | ||||
| # Please do not add any logic to this shell script. Add logic to the go code | ||||
| # that generates the set-gen program. | ||||
| @@ -63,4 +65,18 @@ ${clientgen} --clientset-name=federation_internalclientset --clientset-path=k8s. | ||||
| ${clientgen} --clientset-name=federation_release_1_5 --clientset-path=k8s.io/kubernetes/federation/client/clientset_generated --input="../../federation/apis/federation/v1beta1","api/v1","extensions/v1beta1" --included-types-overrides="api/v1/Service,api/v1/Namespace,extensions/v1beta1/ReplicaSet,api/v1/Secret,extensions/v1beta1/Ingress,extensions/v1beta1/Deployment,extensions/v1beta1/DaemonSet,api/v1/ConfigMap,api/v1/Event"   "$@" | ||||
| ${setgen} "$@" | ||||
|  | ||||
| LISTERGEN_APIS=( | ||||
| pkg/api | ||||
| pkg/api/v1 | ||||
| $( | ||||
|   cd ${KUBE_ROOT} | ||||
|   find pkg/apis -name types.go | xargs dirname | sort | ||||
| ) | ||||
| ) | ||||
|  | ||||
| LISTERGEN_APIS=(${LISTERGEN_APIS[@]/#/k8s.io/kubernetes/}) | ||||
| LISTERGEN_APIS=$(IFS=,; echo "${LISTERGEN_APIS[*]}") | ||||
|  | ||||
| ${listergen} --input-dirs "${LISTERGEN_APIS}" "$@" | ||||
|  | ||||
| # You may add additional calls of code generators like set-gen above. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Andy Goldstein
					Andy Goldstein