mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +00:00
Support references to external types for 3rd party use
This commit is contained in:
parent
c541c69331
commit
86184be5f1
@ -20,20 +20,51 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
|
"github.com/spf13/pflag"
|
||||||
"k8s.io/gengo/args"
|
"k8s.io/gengo/args"
|
||||||
|
"k8s.io/gengo/types"
|
||||||
|
|
||||||
codegenutil "k8s.io/code-generator/pkg/util"
|
codegenutil "k8s.io/code-generator/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ClientGenArgs is a wrapper for arguments to applyconfiguration-gen.
|
||||||
|
type CustomArgs struct {
|
||||||
|
// ExternalApplyConfigurations provides the locations of externally generated
|
||||||
|
// apply configuration types for types referenced by the go structs provided as input.
|
||||||
|
// Locations are provided as a comma separated list of <package>.<typeName>:<applyconfiguration-package>
|
||||||
|
// entries.
|
||||||
|
//
|
||||||
|
// E.g. if a type references appsv1.Deployment, the location of its apply configuration should
|
||||||
|
// be provided:
|
||||||
|
// k8s.io/api/apps/v1.Deployment:k8s.io/client-go/applyconfigurations/apps/v1
|
||||||
|
//
|
||||||
|
// meta/v1 types (TypeMeta and ObjectMeta) are always included and do not need to be passed in.
|
||||||
|
ExternalApplyConfigurations map[types.Name]string
|
||||||
|
}
|
||||||
|
|
||||||
// NewDefaults returns default arguments for the generator.
|
// NewDefaults returns default arguments for the generator.
|
||||||
func NewDefaults() *args.GeneratorArgs {
|
func NewDefaults() (*args.GeneratorArgs, *CustomArgs) {
|
||||||
genericArgs := args.Default().WithoutDefaultFlagParsing()
|
genericArgs := args.Default().WithoutDefaultFlagParsing()
|
||||||
|
customArgs := &CustomArgs{
|
||||||
|
ExternalApplyConfigurations: map[types.Name]string{
|
||||||
|
// Always include TypeMeta and ObjectMeta. They are sufficient for the vast majority of use cases.
|
||||||
|
{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "TypeMeta"}: "k8s.io/client-go/applyconfigurations/meta/v1",
|
||||||
|
{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "ObjectMeta"}: "k8s.io/client-go/applyconfigurations/meta/v1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
genericArgs.CustomArgs = customArgs
|
||||||
|
|
||||||
if pkg := codegenutil.CurrentPackage(); len(pkg) != 0 {
|
if pkg := codegenutil.CurrentPackage(); len(pkg) != 0 {
|
||||||
genericArgs.OutputPackagePath = path.Join(pkg, "pkg/client/applyconfigurations")
|
genericArgs.OutputPackagePath = path.Join(pkg, "pkg/client/applyconfigurations")
|
||||||
}
|
}
|
||||||
|
|
||||||
return genericArgs
|
return genericArgs, customArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ca *CustomArgs) AddFlags(fs *pflag.FlagSet, inputBase string) {
|
||||||
|
pflag.Var(NewExternalApplyConfigurationValue(&ca.ExternalApplyConfigurations, nil), "external-applyconfigurations",
|
||||||
|
"list of comma separated external apply configurations locations in <type-package>.<type-name>:<applyconfiguration-package> form."+
|
||||||
|
"For example: k8s.io/api/apps/v1.Deployment:k8s.io/client-go/applyconfigurations/apps/v1")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate checks the given arguments.
|
// Validate checks the given arguments.
|
||||||
|
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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 args
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/csv"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"k8s.io/gengo/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type externalApplyConfigurationValue struct {
|
||||||
|
externals *map[types.Name]string
|
||||||
|
changed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewExternalApplyConfigurationValue(externals *map[types.Name]string, def []string) *externalApplyConfigurationValue {
|
||||||
|
val := new(externalApplyConfigurationValue)
|
||||||
|
val.externals = externals
|
||||||
|
if def != nil {
|
||||||
|
if err := val.set(def); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ flag.Value = &externalApplyConfigurationValue{}
|
||||||
|
|
||||||
|
func (s *externalApplyConfigurationValue) set(vs []string) error {
|
||||||
|
if !s.changed {
|
||||||
|
*s.externals = map[types.Name]string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, input := range vs {
|
||||||
|
typ, pkg, err := parseExternalMapping(input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, ok := (*s.externals)[typ]; ok {
|
||||||
|
return fmt.Errorf("duplicate type found in --external-applyconfigurations: %v", typ)
|
||||||
|
}
|
||||||
|
(*s.externals)[typ] = pkg
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *externalApplyConfigurationValue) Set(val string) error {
|
||||||
|
vs, err := readAsCSV(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.set(vs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *externalApplyConfigurationValue) Type() string {
|
||||||
|
return "string"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *externalApplyConfigurationValue) String() string {
|
||||||
|
var strs []string
|
||||||
|
for k, v := range *s.externals {
|
||||||
|
strs = append(strs, fmt.Sprintf("%s.%s:%s", k.Package, k.Name, v))
|
||||||
|
}
|
||||||
|
str, _ := writeAsCSV(strs)
|
||||||
|
return "[" + str + "]"
|
||||||
|
}
|
||||||
|
|
||||||
|
func readAsCSV(val string) ([]string, error) {
|
||||||
|
if val == "" {
|
||||||
|
return []string{}, nil
|
||||||
|
}
|
||||||
|
stringReader := strings.NewReader(val)
|
||||||
|
csvReader := csv.NewReader(stringReader)
|
||||||
|
return csvReader.Read()
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeAsCSV(vals []string) (string, error) {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
w := csv.NewWriter(b)
|
||||||
|
err := w.Write(vals)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
w.Flush()
|
||||||
|
return strings.TrimSuffix(b.String(), "\n"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseExternalMapping(mapping string) (typ types.Name, pkg string, err error) {
|
||||||
|
parts := strings.Split(mapping, ":")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return types.Name{}, "", fmt.Errorf("expected string of the form <package>.<typeName>:<applyconfiguration-package> but got %s", mapping)
|
||||||
|
}
|
||||||
|
packageTypeStr := parts[0]
|
||||||
|
pkg = parts[1]
|
||||||
|
ptParts := strings.Split(packageTypeStr, ".")
|
||||||
|
if len(ptParts) != 2 {
|
||||||
|
return types.Name{}, "", fmt.Errorf("expected package and type of the form <package>#<typeName> but got %s", packageTypeStr)
|
||||||
|
}
|
||||||
|
structPkg := ptParts[0]
|
||||||
|
structType := ptParts[1]
|
||||||
|
|
||||||
|
return types.Name{Package: structPkg, Name: structType}, pkg, nil
|
||||||
|
}
|
@ -28,6 +28,7 @@ import (
|
|||||||
"k8s.io/gengo/types"
|
"k8s.io/gengo/types"
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
|
|
||||||
|
applygenargs "k8s.io/code-generator/cmd/applyconfiguration-gen/args"
|
||||||
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
|
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -59,7 +60,8 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
|
|||||||
}
|
}
|
||||||
|
|
||||||
pkgTypes := packageTypesForInputDirs(context, arguments.InputDirs, arguments.OutputPackagePath)
|
pkgTypes := packageTypesForInputDirs(context, arguments.InputDirs, arguments.OutputPackagePath)
|
||||||
refs := refGraphForReachableTypes(pkgTypes)
|
initialTypes := arguments.CustomArgs.(*applygenargs.CustomArgs).ExternalApplyConfigurations
|
||||||
|
refs := refGraphForReachableTypes(pkgTypes, initialTypes)
|
||||||
|
|
||||||
groupVersions := make(map[string]clientgentypes.GroupVersions)
|
groupVersions := make(map[string]clientgentypes.GroupVersions)
|
||||||
groupGoNames := make(map[string]string)
|
groupGoNames := make(map[string]string)
|
||||||
|
@ -28,8 +28,8 @@ type refGraph map[types.Name]string
|
|||||||
|
|
||||||
// refGraphForReachableTypes returns a refGraph that contains all reachable types from
|
// refGraphForReachableTypes returns a refGraph that contains all reachable types from
|
||||||
// the root clientgen types of the provided packages.
|
// the root clientgen types of the provided packages.
|
||||||
func refGraphForReachableTypes(pkgTypes map[string]*types.Package) refGraph {
|
func refGraphForReachableTypes(pkgTypes map[string]*types.Package, initialTypes map[types.Name]string) refGraph {
|
||||||
refs := refGraph{}
|
var refs refGraph = initialTypes
|
||||||
|
|
||||||
// Include only types that are reachable from the root clientgen types.
|
// Include only types that are reachable from the root clientgen types.
|
||||||
// We don't want to generate apply configurations for types that are not reachable from a root
|
// We don't want to generate apply configurations for types that are not reachable from a root
|
||||||
|
@ -32,9 +32,10 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
klog.InitFlags(nil)
|
klog.InitFlags(nil)
|
||||||
genericArgs := generatorargs.NewDefaults()
|
genericArgs, customArgs := generatorargs.NewDefaults()
|
||||||
genericArgs.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(), util.BoilerplatePath())
|
genericArgs.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(), util.BoilerplatePath())
|
||||||
genericArgs.AddFlags(pflag.CommandLine)
|
genericArgs.AddFlags(pflag.CommandLine)
|
||||||
|
customArgs.AddFlags(pflag.CommandLine, "k8s.io/kubernetes/pkg/apis") // TODO: move this input path out of client-gen
|
||||||
if err := flag.Set("logtostderr", "true"); err != nil {
|
if err := flag.Set("logtostderr", "true"); err != nil {
|
||||||
klog.Fatalf("Error: %v", err)
|
klog.Fatalf("Error: %v", err)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user