diff --git a/pkg/apis/folder/v0alpha1/types.go b/pkg/apis/folder/v0alpha1/types.go index 2074ba1aad5..ceaaaf1233b 100644 --- a/pkg/apis/folder/v0alpha1/types.go +++ b/pkg/apis/folder/v0alpha1/types.go @@ -39,8 +39,7 @@ type FolderInfoList struct { // +listType=map // +listMapKey=uid - // TODO: Make a new version with `items` => `infoItems`. I just did this to work around it in dev. - Items []FolderInfo `json:"infoItems,omitempty"` + Items []FolderInfo `json:"items,omitempty"` } // FolderInfo briefly describes a folder -- unlike a folder resource, diff --git a/pkg/apis/folder/v0alpha1/zz_generated.openapi.go b/pkg/apis/folder/v0alpha1/zz_generated.openapi.go index e8324ade423..c3c9f74bfc4 100644 --- a/pkg/apis/folder/v0alpha1/zz_generated.openapi.go +++ b/pkg/apis/folder/v0alpha1/zz_generated.openapi.go @@ -241,7 +241,7 @@ func schema_pkg_apis_folder_v0alpha1_FolderInfoList(ref common.ReferenceCallback Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), }, }, - "infoItems": { + "items": { VendorExtensible: spec.VendorExtensible{ Extensions: spec.Extensions{ "x-kubernetes-list-map-keys": []interface{}{ diff --git a/pkg/apis/folder/v0alpha1/zz_generated.openapi_violation_exceptions.list b/pkg/apis/folder/v0alpha1/zz_generated.openapi_violation_exceptions.list index 24bb3204ec5..f738ebaa1eb 100644 --- a/pkg/apis/folder/v0alpha1/zz_generated.openapi_violation_exceptions.list +++ b/pkg/apis/folder/v0alpha1/zz_generated.openapi_violation_exceptions.list @@ -1,3 +1,2 @@ API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/folder/v0alpha1,DescendantCounts,Counts API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/folder/v0alpha1,FolderInfoList,Items -API rule violation: names_match,github.com/grafana/grafana/pkg/apis/folder/v0alpha1,FolderInfoList,Items diff --git a/pkg/registry/apis/provisioning/resources/fileformat_test.go b/pkg/registry/apis/provisioning/resources/fileformat_test.go index 2d6e2063608..8030ee8dd7b 100644 --- a/pkg/registry/apis/provisioning/resources/fileformat_test.go +++ b/pkg/registry/apis/provisioning/resources/fileformat_test.go @@ -85,7 +85,7 @@ spec: require.Equal(t, provisioning.ClassicDashboard, classic) require.Equal(t, &schema.GroupVersionKind{ Group: "dashboard.grafana.app", - Version: "v0alpha1", + Version: "v1alpha1", Kind: "Dashboard", }, gvk) require.NotNil(t, obj) @@ -121,7 +121,7 @@ spec: require.Equal(t, provisioning.ClassicDashboard, parsed.Classic) require.Equal(t, &schema.GroupVersionKind{ Group: "dashboard.grafana.app", - Version: "v0alpha1", + Version: "v1alpha1", Kind: "Dashboard", }, parsed.GVK) diff --git a/pkg/services/apiserver/builder/openapi.go b/pkg/services/apiserver/builder/openapi.go index 0830af9838c..78089f44628 100644 --- a/pkg/services/apiserver/builder/openapi.go +++ b/pkg/services/apiserver/builder/openapi.go @@ -47,6 +47,7 @@ func getOpenAPIPostProcessor(version string, builders []APIGroupBuilder) func(*s if s.Paths == nil { return s, nil } + for _, b := range builders { gv := b.GetGroupVersion() prefix := "/apis/" + gv.String() + "/" @@ -65,6 +66,13 @@ func getOpenAPIPostProcessor(version string, builders []APIGroupBuilder) func(*s Paths: s.Paths, } + // Remove the growing list of kinds + for k, v := range copy.Components.Schemas { + if strings.HasPrefix(k, "io.k8s.apimachinery.pkg.apis.meta.v1") && v.Extensions != nil { + delete(v.Extensions, "x-kubernetes-group-version-kind") // a growing list of everything + } + } + // Optionally include raw http handlers provider, ok := b.(APIGroupRouteProvider) if ok && provider != nil { @@ -110,6 +118,7 @@ func getOpenAPIPostProcessor(version string, builders []APIGroupBuilder) func(*s return ©, nil } } + return s, nil } } diff --git a/pkg/tests/apis/provisioning/testdata/openapi.json b/pkg/tests/apis/provisioning/testdata/openapi.json index 17fa855d76f..6528fcdff86 100644 --- a/pkg/tests/apis/provisioning/testdata/openapi.json +++ b/pkg/tests/apis/provisioning/testdata/openapi.json @@ -705,6 +705,15 @@ "uniqueItems": true } }, + { + "name": "ignoreStoreReadErrorWithClusterBreakingPotential", + "in": "query", + "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, { "name": "labelSelector", "in": "query", @@ -993,6 +1002,15 @@ "uniqueItems": true } }, + { + "name": "ignoreStoreReadErrorWithClusterBreakingPotential", + "in": "query", + "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, { "name": "orphanDependents", "in": "query", @@ -3487,14 +3505,6 @@ "token": { "description": "Token for accessing the repository.", "type": "string" - }, - "webhookSecret": { - "description": "WebhookSecret is the secret used to validate incoming webhooks.", - "type": "string" - }, - "webhookURL": { - "description": "WebhookURL is the URL to send webhooks events to. By default, the system will generate a URL for you but you can use this one to run grafana locally and test the webhooks.", - "type": "string" } } }, @@ -3505,12 +3515,7 @@ ], "properties": { "checked": { - "description": "When the sync job started", - "type": "integer", - "format": "int64" - }, - "generation": { - "description": "The generation (spec changed) that triggered this health check", + "description": "When the health was checked last time", "type": "integer", "format": "int64" }, @@ -3901,8 +3906,10 @@ "description": "The status of a Repository. This is expected never to be created by a kubectl call or similar, and is expected to rarely (if ever) be edited manually. As such, it is also a little less well structured than the spec, such as conditional-but-ever-present fields.", "type": "object", "required": [ + "observedGeneration", "health", - "sync" + "sync", + "webhook" ], "properties": { "health": { @@ -3914,14 +3921,28 @@ } ] }, + "observedGeneration": { + "description": "The generation of the spec last time reconciliation ran", + "type": "integer", + "format": "int64", + "default": 0 + }, "sync": { - "description": "Sync information", + "description": "Sync information with the last sync information", "default": {}, "allOf": [ { "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.SyncStatus" } ] + }, + "webhook": { + "description": "Webhook Information (if applicable)", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.WebhookStatus" + } + ] } } }, @@ -4240,6 +4261,28 @@ } ] }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.WebhookStatus": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "secret": { + "type": "string" + }, + "subscribedEvents": { + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "url": { + "type": "string" + } + } + }, "io.k8s.apimachinery.pkg.apis.meta.v1.APIResource": { "description": "APIResource specifies the name of a resource and whether it is namespaced.", "type": "object", @@ -4345,14 +4388,7 @@ }, "x-kubernetes-list-type": "atomic" } - }, - "x-kubernetes-group-version-kind": [ - { - "group": "", - "kind": "APIResourceList", - "version": "v1" - } - ] + } }, "io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions": { "description": "DeleteOptions may be provided when deleting an API object.", @@ -4376,6 +4412,10 @@ "type": "integer", "format": "int64" }, + "ignoreStoreReadErrorWithClusterBreakingPotential": { + "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", + "type": "boolean" + }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", "type": "string" @@ -4396,309 +4436,7 @@ "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", "type": "string" } - }, - "x-kubernetes-group-version-kind": [ - { - "group": "", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "admissionregistration.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "admissionregistration.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "admissionregistration.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "apps", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "apps", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "apps", - "kind": "DeleteOptions", - "version": "v1beta2" - }, - { - "group": "authentication.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "authentication.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "authentication.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "authorization.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "authorization.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "autoscaling", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "autoscaling", - "kind": "DeleteOptions", - "version": "v2" - }, - { - "group": "autoscaling", - "kind": "DeleteOptions", - "version": "v2beta1" - }, - { - "group": "autoscaling", - "kind": "DeleteOptions", - "version": "v2beta2" - }, - { - "group": "batch", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "batch", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "certificates.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "certificates.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "certificates.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "coordination.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "coordination.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "coordination.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "dashboard.grafana.app", - "kind": "DeleteOptions", - "version": "v0alpha1" - }, - { - "group": "dashboard.grafana.app", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "dashboard.grafana.app", - "kind": "DeleteOptions", - "version": "v2alpha1" - }, - { - "group": "discovery.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "discovery.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "events.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "events.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "extensions", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "featuretoggle.grafana.app", - "kind": "DeleteOptions", - "version": "v0alpha1" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta2" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta3" - }, - { - "group": "folder.grafana.app", - "kind": "DeleteOptions", - "version": "v0alpha1" - }, - { - "group": "iam.grafana.app", - "kind": "DeleteOptions", - "version": "v0alpha1" - }, - { - "group": "internal.apiserver.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "networking.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "networking.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "networking.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "node.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "node.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "node.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "policy", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "policy", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "provisioning.grafana.app", - "kind": "DeleteOptions", - "version": "v0alpha1" - }, - { - "group": "rbac.authorization.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "rbac.authorization.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "rbac.authorization.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "resource.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha3" - }, - { - "group": "scheduling.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "scheduling.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "scheduling.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "storage.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "storage.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "storage.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "storagemigration.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - } - ] + } }, "io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1": { "description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:\u003cname\u003e', where \u003cname\u003e is the name of a field in a struct, or key in a map 'v:\u003cvalue\u003e', where \u003cvalue\u003e is the exact json formatted value of a list item 'i:\u003cindex\u003e', where \u003cindex\u003e is position of a item in a list 'k:\u003ckeys\u003e', where \u003ckeys\u003e is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff", @@ -4988,14 +4726,7 @@ "description": "Status of the operation. One of: \"Success\" or \"Failure\". More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", "type": "string" } - }, - "x-kubernetes-group-version-kind": [ - { - "group": "", - "kind": "Status", - "version": "v1" - } - ] + } }, "io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause": { "description": "StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.", @@ -5080,309 +4811,7 @@ "type": "string", "default": "" } - }, - "x-kubernetes-group-version-kind": [ - { - "group": "", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "admissionregistration.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "admissionregistration.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "admissionregistration.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "apps", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "apps", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "apps", - "kind": "WatchEvent", - "version": "v1beta2" - }, - { - "group": "authentication.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "authentication.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "authentication.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "authorization.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "authorization.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "autoscaling", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "autoscaling", - "kind": "WatchEvent", - "version": "v2" - }, - { - "group": "autoscaling", - "kind": "WatchEvent", - "version": "v2beta1" - }, - { - "group": "autoscaling", - "kind": "WatchEvent", - "version": "v2beta2" - }, - { - "group": "batch", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "batch", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "certificates.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "certificates.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "certificates.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "coordination.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "coordination.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "coordination.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "dashboard.grafana.app", - "kind": "WatchEvent", - "version": "v0alpha1" - }, - { - "group": "dashboard.grafana.app", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "dashboard.grafana.app", - "kind": "WatchEvent", - "version": "v2alpha1" - }, - { - "group": "discovery.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "discovery.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "events.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "events.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "extensions", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "featuretoggle.grafana.app", - "kind": "WatchEvent", - "version": "v0alpha1" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "WatchEvent", - "version": "v1beta2" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "WatchEvent", - "version": "v1beta3" - }, - { - "group": "folder.grafana.app", - "kind": "WatchEvent", - "version": "v0alpha1" - }, - { - "group": "iam.grafana.app", - "kind": "WatchEvent", - "version": "v0alpha1" - }, - { - "group": "internal.apiserver.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "networking.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "networking.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "networking.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "node.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "node.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "node.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "policy", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "policy", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "provisioning.grafana.app", - "kind": "WatchEvent", - "version": "v0alpha1" - }, - { - "group": "rbac.authorization.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "rbac.authorization.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "rbac.authorization.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "resource.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha3" - }, - { - "group": "scheduling.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "scheduling.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "scheduling.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "storage.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "storage.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "storage.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "storagemigration.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - } - ] + } }, "io.k8s.apimachinery.pkg.runtime.RawExtension": { "description": "RawExtension is used to hold extensions in external versions.\n\nTo use this, make a field which has RawExtension as its type in your external, versioned struct, and Object in your internal struct. You also need to register your various plugin types.\n\n// Internal package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.Object `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// External package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.RawExtension `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// On the wire, the JSON will look something like this:\n\n\t{\n\t\t\"kind\":\"MyAPIObject\",\n\t\t\"apiVersion\":\"v1\",\n\t\t\"myPlugin\": {\n\t\t\t\"kind\":\"PluginA\",\n\t\t\t\"aOption\":\"foo\",\n\t\t},\n\t}\n\nSo what happens? Decode first uses json or yaml to unmarshal the serialized data into your external MyAPIObject. That causes the raw JSON to be stored, but not unpacked. The next step is to copy (using pkg/conversion) into the internal struct. The runtime package's DefaultScheme has conversion functions installed which will unpack the JSON stored in RawExtension, turning it into the correct object type, and storing it in the Object. (TODO: In the case where the object is of an unknown type, a runtime.Unknown object will be created and stored.)",