Go structs to OpenAPI to JSONSchema generation aut

2019-07-10 08:22发布

问题:

I have a Go struct for which I want to generate an OpenAPI schema automatically. Once I have an OpenAPI definition of that struct I wanna generate JSONSchema of it, so that I can validate the input data that comes and is gonna be parsed into those structs.

The struct looks like the following:

// mySpec: io.myapp.MinimalPod
type MinimalPod struct {
    Name string `json:"name"`

    // k8s: io.k8s.kubernetes.pkg.api.v1.PodSpec
    v1.PodSpec    
}

Above struct is clearly an augmentation of what Kubernetes PodSpec is.

Now the approach that I have used is to generate definition part for my struct MinimalPod, the definition for PodSpec will come from upstream OpenAPI spec of Kubernetes. PodSpec has a key io.k8s.kubernetes.pkg.api.v1.PodSpec in the upstream OpenAPI spec, this definition is injected from there in my Properties. Now in my code that parses above struct I have templates of what to do if struct field is string.

If the field has a comment that starts with k8s: ... the next part is Kubernetes object's OpenAPI definition key. In our case the OpenAPI definition key is io.k8s.kubernetes.pkg.api.v1.PodSpec. So I retrieve that field's definition from the upstream OpenAPI definition and embed it into the definition of my struct.

Once I have generated an OpenAPI definition for this struct which is injected in Kubernetes OpenAPI schema's definition with key being io.myapp.MinimalPod. Now I can use the tool openapi2jsonschema to generate JSONSchema out of this one. Which generates a JSONSchema file named MinimalPod.json.

Now jsonschema tool and the file MinimalPod.json can be used for validating input given to my tool parser to see if all fields were given right.

Is this the right approach of doing things, or is there a tool/library and if I feed Go structs to it, it gives me OpenAPI schema? It would be fine if it does not identify where to inject Kubernetes OpenAPI schema from even automatic parsing of Go structs and giving OpenAPI definition would be much appreciated.


Update 1

After following @mehdy 's instructions, this is what I have tried:

I have used this import path github.com/kedgeproject/kedge/vendor/k8s.io/client-go/pkg/api/v1 to import the PodSpec definition instead of k8s.io/api/core/v1 and code looks like this:

package foomodel

import "github.com/kedgeproject/kedge/vendor/k8s.io/client-go/pkg/api/v1"

// MinimalPod is a minimal pod.
// +k8s:openapi-gen=true
type MinimalPod struct {
        Name string `json:"name"`

        v1.PodSpec
}

Now when I generate the same with flag -i changed from k8s.io/api/core/v1 to github.com/kedgeproject/kedge/vendor/k8s.io/client-go/pkg/api/v1

$ go run example/openapi-gen/main.go -i k8s.io/kube-openapi/example/model,github.com/kedgeproject/kedge/vendor/k8s.io/client-go/pkg/api/v1 -h example/foomodel/header.txt -p k8s.io/kube-openapi/example/foomodel

This is what is generated:

$ cat openapi_generated.go 
// +build !ignore_autogenerated

/*
======

Some random text


======
*/

// This file was autogenerated by openapi-gen. Do not edit it manually!

package foomodel

import (
        spec "github.com/go-openapi/spec"
        common "k8s.io/kube-openapi/pkg/common"
)

func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
        return map[string]common.OpenAPIDefinition{
                "k8s.io/kube-openapi/example/model.Container": {
                        Schema: spec.Schema{
                                SchemaProps: spec.SchemaProps{
                                        Description: "Container defines a single application container that you want to run within a pod.",
                                        Properties: map[string]spec.Schema{
                                                "health": {
                                                        SchemaProps: spec.SchemaProps{
                                                                Description: "One common definitions for 'livenessProbe' and 'readinessProbe' this allows to have only one place to define both probes (if they are the same) Periodic probe of container liveness and readiness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
                                                                Ref:         ref("k8s.io/client-go/pkg/api/v1.Probe"),
                                                        },
                                                },
                                                "Container": {
                                                        SchemaProps: spec.SchemaProps{
                                                                Ref: ref("k8s.io/client-go/pkg/api/v1.Container"),
                                                        },
                                                },
                                        },
                                        Required: []string{"Container"},
                                },
                        },
                        Dependencies: []string{
                                "k8s.io/client-go/pkg/api/v1.Container", "k8s.io/client-go/pkg/api/v1.Probe"},
                },
        }
}

I get only this much of the configuration generated. While when I switch back to "k8s.io/api/core/v1" I get config code auto generated which is more than 8k lines. What am I missing here?

Here definition of k8s.io/client-go/pkg/api/v1.Container and k8s.io/client-go/pkg/api/v1.Probe is missing while when I use k8s.io/api/core/v1 as import everything is generated.

Note: To generate above steps, please git clone https://github.com/kedgeproject/kedge in GOPATH.

回答1:

You can use kube-openapi package for this. I am going to add a sample to the repo but I've tested this simple model:

// Car is a simple car model.
// +k8s:openapi-gen=true
type Car struct {
    Color    string
    Capacity int
    // +k8s:openapi-gen=false
    HiddenFeature string
}

If you assume you created this file in

go run example/openapi-gen/main.go -h example/model/header.txt -i k8s.io/kube-openapi/example/model -p k8s.io/kube-openapi/example/model

(you also need to add a header.txt file). You should see a new file created in example/model folder called openapi_generated.go. This is an intermediate generated file that has your OpenAPI model in it:

func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
    return map[string]common.OpenAPIDefinition{
        "k8s.io/kube-openapi/example/model.Car": {
            Schema: spec.Schema{
                SchemaProps: spec.SchemaProps{
                    Description: "Car is a simple car model.",
                    Properties: map[string]spec.Schema{
                        "Color": {
                            SchemaProps: spec.SchemaProps{
                                Type:   []string{"string"},
                                Format: "",
                            },
                        },
                        "Capacity": {
                            SchemaProps: spec.SchemaProps{
                                Type:   []string{"integer"},
                                Format: "int32",
                            },
                        },
                    },
                    Required: []string{"Color", "Capacity"},
                },
            },
            Dependencies: []string{},
        },
    }
}

From there you should be able to call the generated method, get the model for your Type and get its Schema.

With some go get magic and changing the command line a little, I was able to generate the model for your model. Here is what you should change in your code:

package model

import "k8s.io/api/core/v1"

// MinimalPod is a minimal pod.
// +k8s:openapi-gen=true
type MinimalPod struct {
    Name string `json:"name"`

    v1.PodSpec
}

and then change the run command a little to include PodSpec in the generation:

go run example/openapi-gen/main.go -h example/model/header.txt -i k8s.io/kube-openapi/example/model,k8s.io/api/core/v1 -p k8s.io/kube-openapi/example/model

Here is what I got: https://gist.github.com/mbohlool/e399ac2458d12e48cc13081289efc55a