mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +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"
|
||||
"path"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/gengo/args"
|
||||
"k8s.io/gengo/types"
|
||||
|
||||
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.
|
||||
func NewDefaults() *args.GeneratorArgs {
|
||||
func NewDefaults() (*args.GeneratorArgs, *CustomArgs) {
|
||||
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 {
|
||||
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.
|
||||
|
@ -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/klog/v2"
|
||||
|
||||
applygenargs "k8s.io/code-generator/cmd/applyconfiguration-gen/args"
|
||||
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)
|
||||
refs := refGraphForReachableTypes(pkgTypes)
|
||||
initialTypes := arguments.CustomArgs.(*applygenargs.CustomArgs).ExternalApplyConfigurations
|
||||
refs := refGraphForReachableTypes(pkgTypes, initialTypes)
|
||||
|
||||
groupVersions := make(map[string]clientgentypes.GroupVersions)
|
||||
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
|
||||
// the root clientgen types of the provided packages.
|
||||
func refGraphForReachableTypes(pkgTypes map[string]*types.Package) refGraph {
|
||||
refs := refGraph{}
|
||||
func refGraphForReachableTypes(pkgTypes map[string]*types.Package, initialTypes map[types.Name]string) refGraph {
|
||||
var refs refGraph = initialTypes
|
||||
|
||||
// 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
|
||||
|
@ -32,9 +32,10 @@ import (
|
||||
|
||||
func main() {
|
||||
klog.InitFlags(nil)
|
||||
genericArgs := generatorargs.NewDefaults()
|
||||
genericArgs, customArgs := generatorargs.NewDefaults()
|
||||
genericArgs.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(), util.BoilerplatePath())
|
||||
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 {
|
||||
klog.Fatalf("Error: %v", err)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user