Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,5 @@ require (
gopkg.in/yaml.v2 v2.2.1
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92
sigs.k8s.io/structured-merge-diff v0.0.0-20190416230737-b2ed7e1d99f6
sigs.k8s.io/structured-merge-diff v0.0.0-20190426204423-ea680f03cc65
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,5 @@ k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6 h1:4s3/R4+OYYYUKptXPhZKjQ04WJ6Eh
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92 h1:PgoMI/L1Nu5Vmvgm+vGheLuxKST8h6FMOqggyAFtHPc=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
sigs.k8s.io/structured-merge-diff v0.0.0-20190416230737-b2ed7e1d99f6 h1:HxGn4yiP+T5dFdwGo2WX/Jsi3OAjSuSkFLF/7QsshxI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190416230737-b2ed7e1d99f6/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190426204423-ea680f03cc65 h1:xJNnO2qzHtgVCSPoGkkltSpyEX7D7IJw1TmbE3G/7lY=
sigs.k8s.io/structured-merge-diff v0.0.0-20190426204423-ea680f03cc65/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
111 changes: 111 additions & 0 deletions pkg/schemaconv/smd.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"errors"
"fmt"
"path"
"sort"
"strings"

"k8s.io/kube-openapi/pkg/util/proto"
Expand Down Expand Up @@ -145,6 +146,109 @@ func (c *convert) makeRef(model proto.Schema) schema.TypeRef {
return tr
}

func makeUnions(extensions map[string]interface{}) ([]schema.Union, error) {
schemaUnions := []schema.Union{}
if iunions, ok := extensions["x-kubernetes-unions"]; ok {
unions, ok := iunions.([]interface{})
if !ok {
return nil, fmt.Errorf(`"x-kubernetes-unions" should be a list, got %#v`, unions)
}
for _, iunion := range unions {
union, ok := iunion.(map[interface{}]interface{})
if !ok {
return nil, fmt.Errorf(`"x-kubernetes-unions" items should be a map of string to unions, got %#v`, iunion)
}
unionMap := map[string]interface{}{}
for k, v := range union {
key, ok := k.(string)
if !ok {
return nil, fmt.Errorf(`"x-kubernetes-unions" has non-string key: %#v`, k)
}
unionMap[key] = v
}
schemaUnion, err := makeUnion(unionMap)
if err != nil {
return nil, err
}
schemaUnions = append(schemaUnions, schemaUnion)
}
}

// Make sure we have no overlap between unions
fs := map[string]struct{}{}
for _, u := range schemaUnions {
if u.Discriminator != nil {
if _, ok := fs[*u.Discriminator]; ok {
return nil, fmt.Errorf("%v field appears multiple times in unions", *u.Discriminator)
}
fs[*u.Discriminator] = struct{}{}
}
for _, f := range u.Fields {
if _, ok := fs[f.FieldName]; ok {
return nil, fmt.Errorf("%v field appears multiple times in unions", f.FieldName)
}
fs[f.FieldName] = struct{}{}
}
}

return schemaUnions, nil
}

func makeUnion(extensions map[string]interface{}) (schema.Union, error) {
union := schema.Union{
Fields: []schema.UnionField{},
}

if idiscriminator, ok := extensions["discriminator"]; ok {
discriminator, ok := idiscriminator.(string)
if !ok {
return schema.Union{}, fmt.Errorf(`"discriminator" must be a string, got: %#v`, idiscriminator)
}
union.Discriminator = &discriminator
}

if ifields, ok := extensions["fields-discriminated"]; ok {
fields, ok := ifields.(map[interface{}]interface{})
if !ok {
return schema.Union{}, fmt.Errorf(`"fields-discriminated" must be a map[string]string, got: %#v`, ifields)
}
// Needs sorted keys by field.
keys := []string{}
for ifield := range fields {
field, ok := ifield.(string)
if !ok {
return schema.Union{}, fmt.Errorf(`"fields-discriminated": field must be a string, got: %#v`, ifield)
}
keys = append(keys, field)

}
sort.Strings(keys)
reverseMap := map[string]struct{}{}
for _, field := range keys {
value := fields[field]
discriminated, ok := value.(string)
if !ok {
return schema.Union{}, fmt.Errorf(`"fields-discriminated"/%v: value must be a string, got: %#v`, field, value)
}
union.Fields = append(union.Fields, schema.UnionField{
FieldName: field,
DiscriminatedBy: discriminated,
})

// Check that we don't have the same discriminatedBy multiple times.
if _, ok := reverseMap[discriminated]; ok {
return schema.Union{}, fmt.Errorf("Multiple fields have the same discriminated name: %v", discriminated)
}
reverseMap[discriminated] = struct{}{}
}
}

if union.Discriminator != nil && len(union.Fields) == 0 {
return schema.Union{}, fmt.Errorf("discriminator set to %v, but no fields in union", *union.Discriminator)
}
return union, nil
}

func (c *convert) VisitKind(k *proto.Kind) {
a := c.top()
a.Struct = &schema.Struct{}
Expand All @@ -157,6 +261,13 @@ func (c *convert) VisitKind(k *proto.Kind) {
})
}

unions, err := makeUnions(k.GetExtensions())
if err != nil {
c.reportError(err.Error())
return
}
a.Struct.Unions = unions

// TODO: Get element relationship when we start adding it to the spec.
}

Expand Down
61 changes: 61 additions & 0 deletions pkg/schemaconv/testdata/new-schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,11 @@ types:
- name: type
type:
scalar: string
unions:
- discriminator: type
fields:
- fieldName: rollingUpdate
discriminatedBy: RollingUpdate
- name: io.k8s.api.apps.v1beta1.RollbackConfig
struct:
fields:
Expand Down Expand Up @@ -6621,6 +6626,62 @@ types:
- name: vsphereVolume
type:
namedType: io.k8s.api.core.v1.VsphereVirtualDiskVolumeSource
unions:
- fields:
- fieldName: awsElasticBlockStore
discriminatedBy: AWSElasticBlockStore
- fieldName: azureDisk
discriminatedBy: AzureDisk
- fieldName: azureFile
discriminatedBy: AzureFile
- fieldName: cephfs
discriminatedBy: CephFS
- fieldName: cinder
discriminatedBy: Cinder
- fieldName: configMap
discriminatedBy: ConfigMap
- fieldName: downwardAPI
discriminatedBy: DownwardAPI
- fieldName: emptyDir
discriminatedBy: EmptyDir
- fieldName: fc
discriminatedBy: FC
- fieldName: flexVolume
discriminatedBy: FlexVolume
- fieldName: flocker
discriminatedBy: Flocker
- fieldName: gcePersistentDisk
discriminatedBy: GCEPersistentDisk
- fieldName: gitRepo
discriminatedBy: GitRepo
- fieldName: glusterfs
discriminatedBy: Glusterfs
- fieldName: hostPath
discriminatedBy: HostPath
- fieldName: iscsi
discriminatedBy: ISCSI
- fieldName: nfs
discriminatedBy: NFS
- fieldName: persistentVolumeClaim
discriminatedBy: PersistentVolumeClaim
- fieldName: photonPersistentDisk
discriminatedBy: PhotonPersistentDisk
- fieldName: portworxVolume
discriminatedBy: PortworxVolume
- fieldName: projected
discriminatedBy: Projected
- fieldName: quobyte
discriminatedBy: Quobyte
- fieldName: rbd
discriminatedBy: RBD
- fieldName: scaleIO
discriminatedBy: ScaleIO
- fieldName: secret
discriminatedBy: Secret
- fieldName: storageos
discriminatedBy: StorageOS
- fieldName: vsphereVolume
discriminatedBy: VsphereVolume
- name: io.k8s.api.core.v1.VolumeDevice
struct:
fields:
Expand Down
37 changes: 37 additions & 0 deletions pkg/schemaconv/testdata/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -78578,6 +78578,12 @@
},
"io.k8s.api.apps.v1beta1.DeploymentStrategy": {
"description": "DeploymentStrategy describes how to replace existing pods with new ones.",
"x-kubernetes-unions": [{
"discriminator": "type",
"fields-discriminated": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this mean we don't support non-discriminator unions? Would rather call it fields instead of fields-discriminated.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as the type is different, we would in fact need fields []string and discriminatedFields map[string]string.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I'll probably do that ... if discriminator is set, then fields must be unset and fields-discriminated must be set. and the other way around.

"rollingUpdate": "RollingUpdate"
}
}],
"properties": {
"rollingUpdate": {
"description": "Rolling update config params. Present only if DeploymentStrategyType = RollingUpdate.",
Expand Down Expand Up @@ -87522,6 +87528,37 @@
},
"io.k8s.api.core.v1.Volume": {
"description": "Volume represents a named volume in a pod that may be accessed by any container in the pod.",
"x-kubernetes-unions": [{
"fields-discriminated": {
"awsElasticBlockStore": "AWSElasticBlockStore",
"azureDisk": "AzureDisk",
"azureFile": "AzureFile",
"cephfs": "CephFS",
"cinder": "Cinder",
"configMap": "ConfigMap",
"downwardAPI": "DownwardAPI",
"emptyDir": "EmptyDir",
"fc": "FC",
"flexVolume": "FlexVolume",
"flocker": "Flocker",
"gcePersistentDisk": "GCEPersistentDisk",
"gitRepo": "GitRepo",
"glusterfs": "Glusterfs",
"hostPath": "HostPath",
"iscsi": "ISCSI",
"nfs": "NFS",
"persistentVolumeClaim": "PersistentVolumeClaim",
"photonPersistentDisk": "PhotonPersistentDisk",
"portworxVolume": "PortworxVolume",
"projected": "Projected",
"quobyte": "Quobyte",
"rbd": "RBD",
"scaleIO": "ScaleIO",
"secret": "Secret",
"storageos": "StorageOS",
"vsphereVolume": "VsphereVolume"
}
}],
"required": [
"name"
],
Expand Down