Skip to content

Commit

Permalink
refactor: misc refactors to the OpenAPI generator
Browse files Browse the repository at this point in the history
Signed-off-by: Marc Nuri <[email protected]>
  • Loading branch information
manusa committed Oct 7, 2024
1 parent 455d43e commit 27b6cff
Show file tree
Hide file tree
Showing 9 changed files with 203 additions and 21,876 deletions.
70 changes: 7 additions & 63 deletions kubernetes-model-generator/openapi/generator/cmd/openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,7 @@ import (
"github.com/fabric8io/kubernetes-client/kubernetes-model-generator/openapi/generator/cmd/generated_openapi"
"github.com/fabric8io/kubernetes-client/kubernetes-model-generator/openapi/generator/pkg/openapi"
"github.com/fabric8io/kubernetes-client/kubernetes-model-generator/openapi/generator/pkg/openshift"
"github.com/fabric8io/kubernetes-client/kubernetes-model-generator/openapi/generator/pkg/parser"
"strings"

networkattachmentdefinition "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
openshiftbaremetaloperatorv1alpha1 "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1"
openshiftclusterapiprovidermetal3v1beta1 "github.com/metal3-io/cluster-api-provider-metal3/api/v1beta1"
openshiftconfigv1 "github.com/openshift/api/config/v1"
openshiftcloudcredentialoperatorv1 "github.com/openshift/cloud-credential-operator/pkg/apis/cloudcredential/v1"
openshiftclusternetworkoperatorv1 "github.com/openshift/cluster-network-operator/pkg/apis/network/v1"
openshiftclusternodetuningoperatorv1 "github.com/openshift/cluster-node-tuning-operator/pkg/apis/tuned/v1"
openshifthivev1 "github.com/openshift/hive/apis/hive/v1"
openshiftinstallerv1 "github.com/openshift/installer/pkg/types"
operatorframeworkv1 "github.com/operator-framework/api/pkg/operators/v1"
operatorframeworkv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
olm "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1"
prometheusoperatorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
"github.com/spf13/cobra"
"k8s.io/kube-openapi/pkg/common"
"k8s.io/kube-openapi/pkg/validation/spec"
"time"
)

Expand All @@ -55,57 +37,19 @@ func init() {

var openApiRun = func(cobraCmd *cobra.Command, args []string) {
startTime := time.Now()
fmt.Printf("OpenAPI JSON schema generation started...\n%s\n", strings.Join([]string{
// Force imports so that modules are present in go.mod
networkattachmentdefinition.SchemeGroupVersion.String(),
olm.SchemeGroupVersion.String(),
openshiftbaremetaloperatorv1alpha1.GroupVersion.String(),
openshiftconfigv1.SchemeGroupVersion.String(),
openshiftcloudcredentialoperatorv1.GroupVersion.String(),
openshiftclusterapiprovidermetal3v1beta1.GroupVersion.String(),
openshiftclusternetworkoperatorv1.GroupVersion.String(),
openshiftclusternodetuningoperatorv1.SchemeGroupVersion.String(),
openshifthivev1.SchemeGroupVersion.String(),
"install.openshift.io/" + openshiftinstallerv1.InstallConfigVersion + " (" + strings.Join(openshiftinstallerv1.PlatformNames, ", ") + ")",
operatorframeworkv1alpha1.SchemeGroupVersion.String(),
operatorframeworkv1.GroupVersion.String(),
prometheusoperatorv1.SchemeGroupVersion.String(),
}, "\n"))
fmt.Println("OpenAPI JSON schema generation started...")
var targetDirectory string
if len(args) > 0 {
targetDirectory = args[0]
} else {
targetDirectory = "."
}
openApiGenerator := openapi.NewGenerator(targetDirectory, "openshift-generated")
openShiftModule := parser.NewModule(openshift.PackagePatterns...)
/////////////////////////////////////////////////////////////////////////////////
// Ported from github.com/openshift/api/openapi/cmd/models-schema/main.go
refFunc := func(name string) spec.Ref {
return spec.MustCreateRef(fmt.Sprintf("#/definitions/%s", openShiftModule.ApiName(name)))
}
defs := generated_openapi.GetOpenAPIDefinitions(refFunc)
for k, v := range defs {
// Marc: Use gengo to complete information for the definition
fabric8Info := openShiftModule.ExtractInfo(k)
if v.Schema.ExtraProps == nil {
v.Schema.ExtraProps = make(map[string]interface{})
}
v.Schema.ExtraProps["x-fabric8-info"] = fabric8Info

// Replace top-level schema with v2 if a v2 schema is embedded
// so that the output of this program is always in OpenAPI v2.
// This is done by looking up an extension that marks the embedded v2
// schema, and, if the v2 schema is found, make it the resulting schema for
// the type.
if schema, ok := v.Schema.Extensions[common.ExtensionV2Schema]; ok {
if v2Schema, isOpenAPISchema := schema.(spec.Schema); isOpenAPISchema {
openApiGenerator.PutDefinition(openShiftModule.ApiName(k), v2Schema)
continue
}
}
openApiGenerator.PutDefinition(openShiftModule.ApiName(k), v.Schema)
}
openApiGenerator := openapi.NewGenerator(
targetDirectory,
"openshift-generated",
generated_openapi.GetOpenAPIDefinitions,
openshift.PackagePatterns...,
)
if err := openApiGenerator.WriteDefinitions(); err != nil {
panic(fmt.Errorf("error writing OpenAPI schema: %w", err))
}
Expand Down
64 changes: 64 additions & 0 deletions kubernetes-model-generator/openapi/generator/cmd/supported-apis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* 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 (
"fmt"
networkattachmentdefinition "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
openshiftbaremetaloperatorv1alpha1 "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1"
openshiftclusterapiprovidermetal3v1beta1 "github.com/metal3-io/cluster-api-provider-metal3/api/v1beta1"
openshiftconfigv1 "github.com/openshift/api/config/v1"
openshiftcloudcredentialoperatorv1 "github.com/openshift/cloud-credential-operator/pkg/apis/cloudcredential/v1"
openshiftclusternetworkoperatorv1 "github.com/openshift/cluster-network-operator/pkg/apis/network/v1"
openshiftclusternodetuningoperatorv1 "github.com/openshift/cluster-node-tuning-operator/pkg/apis/tuned/v1"
openshifthivev1 "github.com/openshift/hive/apis/hive/v1"
openshiftinstallerv1 "github.com/openshift/installer/pkg/types"
operatorframeworkv1 "github.com/operator-framework/api/pkg/operators/v1"
operatorframeworkv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
olm "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1"
prometheusoperatorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
"github.com/spf13/cobra"
"strings"
)

var supportedApis = &cobra.Command{
Use: "supported-apis",
Short: "Lists the APIs supported by this generator",
Run: supportedApisRun,
}

func init() {
rootCmd.AddCommand(supportedApis)
}

// Forces imports so that modules are present in go.mod
var supportedApisRun = func(cobraCmd *cobra.Command, args []string) {
fmt.Printf("This generator generates OpenAPI schemas for the following supported APIs:\n%s\n", strings.Join([]string{
networkattachmentdefinition.SchemeGroupVersion.String(),
olm.SchemeGroupVersion.String(),
openshiftbaremetaloperatorv1alpha1.GroupVersion.String(),
openshiftconfigv1.SchemeGroupVersion.String(),
openshiftcloudcredentialoperatorv1.GroupVersion.String(),
openshiftclusterapiprovidermetal3v1beta1.GroupVersion.String(),
openshiftclusternetworkoperatorv1.GroupVersion.String(),
openshiftclusternodetuningoperatorv1.SchemeGroupVersion.String(),
openshifthivev1.SchemeGroupVersion.String(),
"install.openshift.io/" + openshiftinstallerv1.InstallConfigVersion + " (" + strings.Join(openshiftinstallerv1.PlatformNames, ", ") + ")",
operatorframeworkv1alpha1.SchemeGroupVersion.String(),
operatorframeworkv1.GroupVersion.String(),
prometheusoperatorv1.SchemeGroupVersion.String(),
}, "\n"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,73 @@ package openapi
import (
"encoding/json"
"fmt"
"github.com/fabric8io/kubernetes-client/kubernetes-model-generator/openapi/generator/pkg/parser"
"k8s.io/kube-openapi/pkg/common"
"k8s.io/kube-openapi/pkg/validation/spec"
"os"
"path/filepath"
"strings"
)

type JsonGenerator struct {
type JsonGenerator interface {
WriteDefinitions() error
}

type jsonGenerator struct {
name string
targetDirectory string
definitions map[string]spec.Schema
parserModule *parser.Module
}

func NewGenerator(targetDirectory string, name string) *JsonGenerator {
return &JsonGenerator{
func NewGenerator(
targetDirectory string,
name string,
getDefinitionsFunc func(callback common.ReferenceCallback) map[string]common.OpenAPIDefinition,
patterns ...string,
) JsonGenerator {
g := &jsonGenerator{
name: name,
targetDirectory: targetDirectory,
definitions: make(map[string]spec.Schema),
parserModule: parser.NewModule(patterns...),
}
defs := getDefinitionsFunc(g.refFunc)
// Ported and adapted from github.com/openshift/api/openapi/cmd/models-schema/main.go
// https://github.com/openshift/api/blob/b1f700bdd8d22c4033be4e4e9ef751d89ade42e8/openapi/cmd/models-schema/main.go#L30-L44
for k, v := range defs {
g.addFabric8Info(k, v)
// Replace top-level schema with v2 if a v2 schema is embedded
// so that the output of this program is always in OpenAPI v2.
// This is done by looking up an extension that marks the embedded v2
// schema, and, if the v2 schema is found, make it the resulting schema for
// the type.
if schema, ok := v.Schema.Extensions[common.ExtensionV2Schema]; ok {
if v2Schema, isOpenAPISchema := schema.(spec.Schema); isOpenAPISchema {
g.definitions[g.parserModule.ApiName(k)] = v2Schema
continue
}
}
g.definitions[g.parserModule.ApiName(k)] = v.Schema
}
return g
}

func (g *JsonGenerator) PutDefinition(name string, schema spec.Schema) {
g.definitions[name] = schema
// Ported and adapted from github.com/openshift/api/openapi/cmd/models-schema/main.go
// https://github.com/openshift/api/blob/b1f700bdd8d22c4033be4e4e9ef751d89ade42e8/openapi/cmd/models-schema/main.go#L25-L27
func (g *jsonGenerator) refFunc(name string) spec.Ref {
return spec.MustCreateRef(fmt.Sprintf("#/definitions/%s", g.parserModule.ApiName(name)))
}

func (g *JsonGenerator) WriteDefinitions() error {
func (g *jsonGenerator) addFabric8Info(k string, v common.OpenAPIDefinition) {
// Marc: Use gengo to complete information for the definition
fabric8Info := g.parserModule.ExtractInfo(k)
if v.Schema.ExtraProps == nil {
v.Schema.ExtraProps = make(map[string]interface{})
}
v.Schema.ExtraProps["x-fabric8-info"] = fabric8Info
}

func (g *jsonGenerator) WriteDefinitions() error {
data, err := json.MarshalIndent(&spec.Swagger{
SwaggerProps: spec.SwaggerProps{
Definitions: g.definitions,
Expand All @@ -66,19 +108,3 @@ func (g *JsonGenerator) WriteDefinitions() error {
}
return nil
}

// FriendlyName returns an OpenAPI friendly name for the given name.
// From vendor/k8s.io/apiserver/pkg/endpoints/openapi/openapi.go
// https://github.com/kubernetes/apiserver/blob/60d1ca672541e1b30b558e32e53cad7c172345a6/pkg/endpoints/openapi/openapi.go#L136-L147
func FriendlyName(name string) string {
nameParts := strings.Split(name, "/")
// Reverse first part. e.g., io.k8s... instead of k8s.io...
if len(nameParts) > 0 && strings.Contains(nameParts[0], ".") {
parts := strings.Split(nameParts[0], ".")
for i, j := 0, len(parts)-1; i < j; i, j = i+1, j-1 {
parts[i], parts[j] = parts[j], parts[i]
}
nameParts[0] = strings.Join(parts, ".")
}
return strings.Join(nameParts, ".")
}
28 changes: 25 additions & 3 deletions kubernetes-model-generator/openapi/generator/pkg/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// Package parser provides functionality to extract information from Go types and packages using gengo.
package parser

import (
"fmt"
"github.com/fabric8io/kubernetes-client/kubernetes-model-generator/openapi/generator/pkg/kubernetes"
"github.com/fabric8io/kubernetes-client/kubernetes-model-generator/openapi/generator/pkg/openapi"
"k8s.io/gengo/v2/parser"
"k8s.io/gengo/v2/types"
"strings"
Expand Down Expand Up @@ -73,17 +74,22 @@ func (oam *Module) ExtractInfo(definitionName string) *Fabric8Info {
return fabric8Info
}

// ApiName returns the completed definition name for the OpenAPI component
// The standard definition name is usually based on the module name + package name + type name.
// However, Kubernetes comment tags include augmented information such as the groupName and versionName.
// This method attempts to resolve the additional information from the gengo processed/parsed package and
// type information.
func (oam *Module) ApiName(definitionName string) string {
// Don't treat k8s.io types, json is expected to contain the full Go definition name instead of the group/version
if strings.HasPrefix(definitionName, "k8s.io/") {
return openapi.FriendlyName(definitionName)
return FriendlyName(definitionName)
}
lastSeparator := strings.LastIndex(definitionName, ".")
typeName := definitionName[lastSeparator+1:]
pkg := oam.resolvePackage(definitionName)
gn := groupName(pkg)
if gn == "" {
return openapi.FriendlyName(definitionName)
return FriendlyName(definitionName)
}
groupParts := strings.Split(gn, ".")
for i, j := 0, len(groupParts)-1; i < j; i, j = i+1, j-1 {
Expand Down Expand Up @@ -171,3 +177,19 @@ func scope(typ *types.Type) string {
}
return scope
}

// FriendlyName returns an OpenAPI friendly name for the given name.
// From vendor/k8s.io/apiserver/pkg/endpoints/openapi/openapi.go
// https://github.com/kubernetes/apiserver/blob/60d1ca672541e1b30b558e32e53cad7c172345a6/pkg/endpoints/openapi/openapi.go#L136-L147
func FriendlyName(name string) string {
nameParts := strings.Split(name, "/")
// Reverse first part. e.g., io.k8s... instead of k8s.io...
if len(nameParts) > 0 && strings.Contains(nameParts[0], ".") {
parts := strings.Split(nameParts[0], ".")
for i, j := 0, len(parts)-1; i < j; i, j = i+1, j-1 {
parts[i], parts[j] = parts[j], parts[i]
}
nameParts[0] = strings.Join(parts, ".")
}
return strings.Join(nameParts, ".")
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,43 +22,13 @@ import (
"fmt"
"github.com/fabric8io/kubernetes-client/kubernetes-model-generator/openapi/generator/pkg/openapi"
"github.com/fabric8io/kubernetes-client/kubernetes-model-generator/openapi/generator/pkg/openshift"
"strings"

networkattachmentdefinition "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
openshiftbaremetaloperatorv1alpha1 "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1"
openshiftclusterapiprovidermetal3v1beta1 "github.com/metal3-io/cluster-api-provider-metal3/api/v1beta1"
openshiftconfigv1 "github.com/openshift/api/config/v1"
openshiftcloudcredentialoperatorv1 "github.com/openshift/cloud-credential-operator/pkg/apis/cloudcredential/v1"
openshiftclusternetworkoperatorv1 "github.com/openshift/cluster-network-operator/pkg/apis/network/v1"
openshiftclusternodetuningoperatorv1 "github.com/openshift/cluster-node-tuning-operator/pkg/apis/tuned/v1"
openshifthivev1 "github.com/openshift/hive/apis/hive/v1"
openshiftinstallerv1 "github.com/openshift/installer/pkg/types"
operatorframeworkv1 "github.com/operator-framework/api/pkg/operators/v1"
operatorframeworkv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
olm "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1"
prometheusoperatorv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
"k8s.io/kube-openapi/cmd/openapi-gen/args"
"time"
)

func main() {
startTime := time.Now()
fmt.Printf("OpenAPI code generation started...\n%s\n", strings.Join([]string{
// Force imports so that modules are present in go.mod
networkattachmentdefinition.SchemeGroupVersion.String(),
olm.SchemeGroupVersion.String(),
openshiftbaremetaloperatorv1alpha1.GroupVersion.String(),
openshiftconfigv1.SchemeGroupVersion.String(),
openshiftcloudcredentialoperatorv1.GroupVersion.String(),
openshiftclusterapiprovidermetal3v1beta1.GroupVersion.String(),
openshiftclusternetworkoperatorv1.GroupVersion.String(),
openshiftclusternodetuningoperatorv1.SchemeGroupVersion.String(),
openshifthivev1.SchemeGroupVersion.String(),
"install.openshift.io/" + openshiftinstallerv1.InstallConfigVersion + " (" + strings.Join(openshiftinstallerv1.PlatformNames, ", ") + ")",
operatorframeworkv1alpha1.SchemeGroupVersion.String(),
operatorframeworkv1.GroupVersion.String(),
prometheusoperatorv1.SchemeGroupVersion.String(),
}, "\n"))
fmt.Println("OpenAPI code generation started...")
err := (&openapi.GoGenerator{
Args: args.Args{
OutputFile: "zz_generated.openapi.go",
Expand Down
Loading

0 comments on commit 27b6cff

Please sign in to comment.