containerized-data-importer/tools/openapi-spec-generator/openapi.go
Michael Henriksen 9e2c79b1e0
move api groups to v1beta1 (#1232)
* move upload.cdi.kubevirt.io API group to v1beta1

Signed-off-by: Michael Henriksen <mhenriks@redhat.com>

* move core api to v1beta1

Signed-off-by: Michael Henriksen <mhenriks@redhat.com>

* fix os-3.11 cluster sync and add functional tests for alpha api

Signed-off-by: Michael Henriksen <mhenriks@redhat.com>

* change more occurences of v1alpha1

Signed-off-by: Michael Henriksen <mhenriks@redhat.com>

* updates after rebase

Signed-off-by: Michael Henriksen <mhenriks@redhat.com>
2020-07-10 15:47:38 +02:00

175 lines
5.2 KiB
Go

package main
import (
"fmt"
"strings"
"github.com/emicklei/go-restful"
restfulspec "github.com/emicklei/go-restful-openapi"
"github.com/go-openapi/spec"
"k8s.io/kube-openapi/pkg/builder"
"k8s.io/kube-openapi/pkg/common"
cdicorev1 "kubevirt.io/containerized-data-importer/pkg/apis/core/v1beta1"
cdiuploadv1 "kubevirt.io/containerized-data-importer/pkg/apis/upload/v1beta1"
)
// code stolen/adapted from https://github.com/kubevirt/kubevirt/blob/master/pkg/util/openapi/openapi.go
func createOpenAPIConfig(webServices []*restful.WebService) restfulspec.Config {
return restfulspec.Config{
WebServices: webServices,
WebServicesURL: "",
APIPath: "/swaggerapi",
PostBuildSwaggerObjectHandler: addInfoToSwaggerObject,
}
}
func addInfoToSwaggerObject(swo *spec.Swagger) {
swo.Info = &spec.Info{
InfoProps: spec.InfoProps{
Title: "KubeVirt Containerized Data Importer API",
Description: "Containerized Data Importer for KubeVirt.",
Contact: &spec.ContactInfo{
Name: "kubevirt-dev",
Email: "kubevirt-dev@googlegroups.com",
URL: "https://github.com/kubevirt/containerized-data-importer",
},
License: &spec.License{
Name: "Apache 2.0",
URL: "https://www.apache.org/licenses/LICENSE-2.0",
},
},
}
swo.SecurityDefinitions = spec.SecurityDefinitions{
"BearerToken": &spec.SecurityScheme{
SecuritySchemeProps: spec.SecuritySchemeProps{
Type: "apiKey",
Name: "authorization",
In: "header",
Description: "Bearer Token authentication",
},
},
}
swo.Security = make([]map[string][]string, 1)
swo.Security[0] = map[string][]string{"BearerToken": {}}
}
func createConfig() *common.Config {
return &common.Config{
CommonResponses: map[int]spec.Response{
401: {
ResponseProps: spec.ResponseProps{
Description: "Unauthorized",
},
},
},
Info: &spec.Info{
InfoProps: spec.InfoProps{
Title: "KubeVirt Containerized Data Importer API",
Description: "Containerized Data Importer for KubeVirt.",
Contact: &spec.ContactInfo{
Name: "kubevirt-dev",
Email: "kubevirt-dev@googlegroups.com",
URL: "https://github.com/kubevirt/containerized-data-importer",
},
License: &spec.License{
Name: "Apache 2.0",
URL: "https://www.apache.org/licenses/LICENSE-2.0",
},
},
},
SecurityDefinitions: &spec.SecurityDefinitions{
"BearerToken": &spec.SecurityScheme{
SecuritySchemeProps: spec.SecuritySchemeProps{
Type: "apiKey",
Name: "authorization",
In: "header",
Description: "Bearer Token authentication",
},
},
},
GetDefinitions: func(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
m := cdicorev1.GetOpenAPIDefinitions(ref)
m2 := cdiuploadv1.GetOpenAPIDefinitions(ref)
for k, v := range m2 {
if _, ok := m[k]; !ok {
m[k] = v
}
}
return m
},
}
}
func loadOpenAPISpec(webServices []*restful.WebService) *spec.Swagger {
config := createConfig()
openapispec, err := builder.BuildOpenAPISpec(webServices, config)
if err != nil {
panic(fmt.Errorf("Failed to build swagger: %s", err))
}
// creationTimestamp, lastProbeTime and lastTransitionTime are deserialized as "null"
// Fix it here until
// https://github.com/kubernetes/kubernetes/issues/66899 is ready
// Otherwise CRDs can't use templates which contain metadata and controllers
// can't set conditions without timestamps
objectMeta, exists := openapispec.Definitions["v1.ObjectMeta"]
if exists {
prop := objectMeta.Properties["creationTimestamp"]
prop.Type = spec.StringOrArray{"string", "null"}
// mask v1.Time as in validation v1.Time override sting,null type
prop.Ref = spec.Ref{}
objectMeta.Properties["creationTimestamp"] = prop
}
for k, s := range openapispec.Definitions {
// allow nullable statuses
if status, found := s.Properties["status"]; found {
if !status.Type.Contains("string") {
definitionName := strings.Split(status.Ref.GetPointer().String(), "/")[2]
object := openapispec.Definitions[definitionName]
object.Nullable = true
openapispec.Definitions[definitionName] = object
}
}
if strings.HasSuffix(k, "Condition") {
prop := s.Properties["lastProbeTime"]
prop.Type = spec.StringOrArray{"string", "null"}
prop.Ref = spec.Ref{}
s.Properties["lastProbeTime"] = prop
prop = s.Properties["lastTransitionTime"]
prop.Type = spec.StringOrArray{"string", "null"}
prop.Ref = spec.Ref{}
s.Properties["lastTransitionTime"] = prop
}
if k == "v1.HTTPGetAction" {
prop := s.Properties["port"]
prop.Type = spec.StringOrArray{"string", "number"}
// As intstr.IntOrString, the ref for that must be masked
prop.Ref = spec.Ref{}
s.Properties["port"] = prop
}
if k == "v1.TCPSocketAction" {
prop := s.Properties["port"]
prop.Type = spec.StringOrArray{"string", "number"}
// As intstr.IntOrString, the ref for that must be masked
prop.Ref = spec.Ref{}
s.Properties["port"] = prop
}
if k == "v1.PersistentVolumeClaimSpec" {
for i, r := range s.Required {
if r == "dataSource" {
s.Required = append(s.Required[:i], s.Required[i+1:]...)
openapispec.Definitions[k] = s
break
}
}
}
}
return openapispec
}