2015-08-20 05:08:26 +00:00
|
|
|
/*
|
|
|
|
Copyright 2015 The Kubernetes Authors All rights reserved.
|
|
|
|
|
|
|
|
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 thirdpartyresourcedata
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2015-09-18 00:43:05 +00:00
|
|
|
"io"
|
2015-08-20 05:08:26 +00:00
|
|
|
"strings"
|
|
|
|
|
2015-09-10 19:30:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/api/latest"
|
2015-08-20 05:08:26 +00:00
|
|
|
"k8s.io/kubernetes/pkg/api/meta"
|
2015-09-09 21:59:11 +00:00
|
|
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
2015-09-29 21:36:47 +00:00
|
|
|
apiutil "k8s.io/kubernetes/pkg/api/util"
|
2015-09-09 22:46:06 +00:00
|
|
|
"k8s.io/kubernetes/pkg/apis/experimental"
|
2015-08-20 05:08:26 +00:00
|
|
|
"k8s.io/kubernetes/pkg/runtime"
|
|
|
|
)
|
|
|
|
|
|
|
|
type thirdPartyResourceDataMapper struct {
|
2015-09-01 05:28:08 +00:00
|
|
|
mapper meta.RESTMapper
|
|
|
|
kind string
|
|
|
|
version string
|
2015-09-09 21:36:19 +00:00
|
|
|
group string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *thirdPartyResourceDataMapper) isThirdPartyResource(resource string) bool {
|
|
|
|
return resource == strings.ToLower(t.kind)+"s"
|
2015-08-20 05:08:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *thirdPartyResourceDataMapper) GroupForResource(resource string) (string, error) {
|
2015-09-09 21:36:19 +00:00
|
|
|
if t.isThirdPartyResource(resource) {
|
|
|
|
return t.group, nil
|
|
|
|
}
|
2015-08-20 05:08:26 +00:00
|
|
|
return t.mapper.GroupForResource(resource)
|
|
|
|
}
|
|
|
|
|
2015-09-29 21:36:47 +00:00
|
|
|
func (t *thirdPartyResourceDataMapper) RESTMapping(kind string, groupVersions ...string) (*meta.RESTMapping, error) {
|
|
|
|
if len(groupVersions) != 1 {
|
|
|
|
return nil, fmt.Errorf("unexpected set of groupVersions: %v", groupVersions)
|
2015-09-01 05:28:08 +00:00
|
|
|
}
|
2015-09-29 21:36:47 +00:00
|
|
|
if groupVersions[0] != apiutil.GetGroupVersion(t.group, t.version) {
|
|
|
|
return nil, fmt.Errorf("unknown version %s expected %s", groupVersions[0], apiutil.GetGroupVersion(t.group, t.version))
|
2015-09-01 05:28:08 +00:00
|
|
|
}
|
|
|
|
if kind != "ThirdPartyResourceData" {
|
|
|
|
return nil, fmt.Errorf("unknown kind %s expected %s", kind, t.kind)
|
|
|
|
}
|
2015-09-17 05:15:05 +00:00
|
|
|
mapping, err := t.mapper.RESTMapping("ThirdPartyResourceData", latest.GroupOrDie("experimental").GroupVersion)
|
2015-08-20 05:08:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
mapping.Codec = NewCodec(mapping.Codec, t.kind)
|
|
|
|
return mapping, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *thirdPartyResourceDataMapper) AliasesForResource(resource string) ([]string, bool) {
|
|
|
|
return t.mapper.AliasesForResource(resource)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *thirdPartyResourceDataMapper) ResourceSingularizer(resource string) (singular string, err error) {
|
|
|
|
return t.mapper.ResourceSingularizer(resource)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *thirdPartyResourceDataMapper) VersionAndKindForResource(resource string) (defaultVersion, kind string, err error) {
|
2015-09-09 21:36:19 +00:00
|
|
|
if t.isThirdPartyResource(resource) {
|
|
|
|
return t.version, t.kind, nil
|
|
|
|
}
|
2015-08-20 05:08:26 +00:00
|
|
|
return t.mapper.VersionAndKindForResource(resource)
|
|
|
|
}
|
|
|
|
|
2015-09-24 01:17:09 +00:00
|
|
|
// ResourceIsValid takes a string (kind) and checks if it's a valid resource
|
|
|
|
func (t *thirdPartyResourceDataMapper) ResourceIsValid(resource string) bool {
|
|
|
|
return t.isThirdPartyResource(resource) || t.mapper.ResourceIsValid(resource)
|
|
|
|
}
|
|
|
|
|
2015-09-09 21:36:19 +00:00
|
|
|
func NewMapper(mapper meta.RESTMapper, kind, version, group string) meta.RESTMapper {
|
2015-09-01 05:28:08 +00:00
|
|
|
return &thirdPartyResourceDataMapper{
|
|
|
|
mapper: mapper,
|
|
|
|
kind: kind,
|
|
|
|
version: version,
|
2015-09-09 21:36:19 +00:00
|
|
|
group: group,
|
2015-09-01 05:28:08 +00:00
|
|
|
}
|
2015-08-20 05:08:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type thirdPartyResourceDataCodec struct {
|
|
|
|
delegate runtime.Codec
|
|
|
|
kind string
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewCodec(codec runtime.Codec, kind string) runtime.Codec {
|
|
|
|
return &thirdPartyResourceDataCodec{codec, kind}
|
|
|
|
}
|
|
|
|
|
2015-09-09 22:46:06 +00:00
|
|
|
func (t *thirdPartyResourceDataCodec) populate(objIn *experimental.ThirdPartyResourceData, data []byte) error {
|
2015-08-20 05:08:26 +00:00
|
|
|
var obj interface{}
|
|
|
|
if err := json.Unmarshal(data, &obj); err != nil {
|
|
|
|
fmt.Printf("Invalid JSON:\n%s\n", string(data))
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
mapObj, ok := obj.(map[string]interface{})
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("unexpected object: %#v", obj)
|
|
|
|
}
|
2015-09-01 05:28:08 +00:00
|
|
|
return t.populateFromObject(objIn, mapObj, data)
|
|
|
|
}
|
|
|
|
|
2015-09-09 22:46:06 +00:00
|
|
|
func (t *thirdPartyResourceDataCodec) populateFromObject(objIn *experimental.ThirdPartyResourceData, mapObj map[string]interface{}, data []byte) error {
|
2015-09-09 21:59:11 +00:00
|
|
|
typeMeta := unversioned.TypeMeta{}
|
2015-09-03 00:13:38 +00:00
|
|
|
if err := json.Unmarshal(data, &typeMeta); err != nil {
|
|
|
|
return err
|
2015-08-20 05:08:26 +00:00
|
|
|
}
|
2015-09-03 00:13:38 +00:00
|
|
|
if typeMeta.Kind != t.kind {
|
|
|
|
return fmt.Errorf("unexpected kind: %s, expected %s", typeMeta.Kind, t.kind)
|
2015-08-20 05:08:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
metadata, ok := mapObj["metadata"].(map[string]interface{})
|
|
|
|
if !ok {
|
2015-09-01 05:28:08 +00:00
|
|
|
return fmt.Errorf("unexpected object for metadata: %#v", mapObj["metadata"])
|
|
|
|
}
|
|
|
|
|
2015-09-03 00:13:38 +00:00
|
|
|
metadataData, err := json.Marshal(metadata)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2015-08-20 05:08:26 +00:00
|
|
|
}
|
2015-09-01 05:28:08 +00:00
|
|
|
|
2015-09-03 00:13:38 +00:00
|
|
|
if err := json.Unmarshal(metadataData, &objIn.ObjectMeta); err != nil {
|
|
|
|
return err
|
2015-09-01 04:11:34 +00:00
|
|
|
}
|
2015-09-01 05:28:08 +00:00
|
|
|
|
2015-08-20 05:08:26 +00:00
|
|
|
objIn.Data = data
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *thirdPartyResourceDataCodec) Decode(data []byte) (runtime.Object, error) {
|
2015-09-09 22:46:06 +00:00
|
|
|
result := &experimental.ThirdPartyResourceData{}
|
2015-08-20 05:08:26 +00:00
|
|
|
if err := t.populate(result, data); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *thirdPartyResourceDataCodec) DecodeToVersion(data []byte, version string) (runtime.Object, error) {
|
|
|
|
// TODO: this is hacky, there must be a better way...
|
|
|
|
obj, err := t.Decode(data)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
objData, err := t.delegate.Encode(obj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return t.delegate.DecodeToVersion(objData, version)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *thirdPartyResourceDataCodec) DecodeInto(data []byte, obj runtime.Object) error {
|
2015-09-09 22:46:06 +00:00
|
|
|
thirdParty, ok := obj.(*experimental.ThirdPartyResourceData)
|
2015-08-20 05:08:26 +00:00
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("unexpected object: %#v", obj)
|
|
|
|
}
|
|
|
|
return t.populate(thirdParty, data)
|
|
|
|
}
|
|
|
|
|
2015-09-01 05:28:08 +00:00
|
|
|
func (t *thirdPartyResourceDataCodec) DecodeIntoWithSpecifiedVersionKind(data []byte, obj runtime.Object, version, kind string) error {
|
2015-09-09 22:46:06 +00:00
|
|
|
thirdParty, ok := obj.(*experimental.ThirdPartyResourceData)
|
2015-08-20 05:08:26 +00:00
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("unexpected object: %#v", obj)
|
|
|
|
}
|
2015-09-01 05:28:08 +00:00
|
|
|
|
|
|
|
if kind != "ThirdPartyResourceData" {
|
|
|
|
return fmt.Errorf("unexpeceted kind: %s", kind)
|
|
|
|
}
|
|
|
|
|
|
|
|
var dataObj interface{}
|
|
|
|
if err := json.Unmarshal(data, &dataObj); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
mapObj, ok := dataObj.(map[string]interface{})
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("unexpcted object: %#v", dataObj)
|
|
|
|
}
|
|
|
|
if kindObj, found := mapObj["kind"]; !found {
|
|
|
|
mapObj["kind"] = kind
|
|
|
|
} else {
|
|
|
|
kindStr, ok := kindObj.(string)
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("unexpected object for 'kind': %v", kindObj)
|
|
|
|
}
|
|
|
|
if kindStr != t.kind {
|
|
|
|
return fmt.Errorf("kind doesn't match, expecting: %s, got %s", kind, kindStr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if versionObj, found := mapObj["apiVersion"]; !found {
|
|
|
|
mapObj["apiVersion"] = version
|
|
|
|
} else {
|
|
|
|
versionStr, ok := versionObj.(string)
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("unexpected object for 'apiVersion': %v", versionObj)
|
|
|
|
}
|
|
|
|
if versionStr != version {
|
|
|
|
return fmt.Errorf("version doesn't match, expecting: %s, got %s", version, versionStr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-20 05:08:26 +00:00
|
|
|
if err := t.populate(thirdParty, data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
const template = `{
|
|
|
|
"kind": "%s",
|
|
|
|
"items": [ %s ]
|
|
|
|
}`
|
|
|
|
|
2015-09-18 00:43:05 +00:00
|
|
|
func encodeToJSON(obj *experimental.ThirdPartyResourceData, stream io.Writer) error {
|
2015-09-03 00:13:38 +00:00
|
|
|
var objOut interface{}
|
|
|
|
if err := json.Unmarshal(obj.Data, &objOut); err != nil {
|
2015-09-18 00:43:05 +00:00
|
|
|
return err
|
2015-09-03 00:13:38 +00:00
|
|
|
}
|
|
|
|
objMap, ok := objOut.(map[string]interface{})
|
|
|
|
if !ok {
|
2015-09-18 00:43:05 +00:00
|
|
|
return fmt.Errorf("unexpected type: %v", objOut)
|
2015-09-03 00:13:38 +00:00
|
|
|
}
|
|
|
|
objMap["metadata"] = obj.ObjectMeta
|
2015-09-18 00:43:05 +00:00
|
|
|
encoder := json.NewEncoder(stream)
|
|
|
|
return encoder.Encode(objMap)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *thirdPartyResourceDataCodec) Encode(obj runtime.Object) ([]byte, error) {
|
|
|
|
buff := &bytes.Buffer{}
|
|
|
|
if err := t.EncodeToStream(obj, buff); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return buff.Bytes(), nil
|
2015-09-03 00:13:38 +00:00
|
|
|
}
|
|
|
|
|
2015-09-18 00:43:05 +00:00
|
|
|
func (t *thirdPartyResourceDataCodec) EncodeToStream(obj runtime.Object, stream io.Writer) (err error) {
|
2015-08-20 05:08:26 +00:00
|
|
|
switch obj := obj.(type) {
|
2015-09-09 22:46:06 +00:00
|
|
|
case *experimental.ThirdPartyResourceData:
|
2015-09-18 00:43:05 +00:00
|
|
|
return encodeToJSON(obj, stream)
|
2015-09-09 22:46:06 +00:00
|
|
|
case *experimental.ThirdPartyResourceDataList:
|
2015-08-20 05:08:26 +00:00
|
|
|
// TODO: There must be a better way to do this...
|
|
|
|
dataStrings := make([]string, len(obj.Items))
|
|
|
|
for ix := range obj.Items {
|
2015-09-18 00:43:05 +00:00
|
|
|
buff := &bytes.Buffer{}
|
|
|
|
err := encodeToJSON(&obj.Items[ix], buff)
|
2015-09-03 00:13:38 +00:00
|
|
|
if err != nil {
|
2015-09-18 00:43:05 +00:00
|
|
|
return err
|
2015-09-03 00:13:38 +00:00
|
|
|
}
|
2015-09-18 00:43:05 +00:00
|
|
|
dataStrings[ix] = buff.String()
|
2015-08-20 05:08:26 +00:00
|
|
|
}
|
2015-09-18 00:43:05 +00:00
|
|
|
fmt.Fprintf(stream, template, t.kind+"List", strings.Join(dataStrings, ","))
|
|
|
|
return nil
|
2015-09-09 21:59:11 +00:00
|
|
|
case *unversioned.Status:
|
2015-09-18 00:43:05 +00:00
|
|
|
return t.delegate.EncodeToStream(obj, stream)
|
2015-08-20 05:08:26 +00:00
|
|
|
default:
|
2015-09-18 00:43:05 +00:00
|
|
|
return fmt.Errorf("unexpected object to encode: %#v", obj)
|
2015-08-20 05:08:26 +00:00
|
|
|
}
|
|
|
|
}
|
2015-09-01 05:28:08 +00:00
|
|
|
|
2015-09-29 21:36:47 +00:00
|
|
|
func NewObjectCreator(group, version string, delegate runtime.ObjectCreater) runtime.ObjectCreater {
|
|
|
|
return &thirdPartyResourceDataCreator{group, version, delegate}
|
2015-09-01 05:28:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type thirdPartyResourceDataCreator struct {
|
2015-09-29 21:36:47 +00:00
|
|
|
group string
|
2015-09-01 05:28:08 +00:00
|
|
|
version string
|
|
|
|
delegate runtime.ObjectCreater
|
|
|
|
}
|
|
|
|
|
2015-09-29 21:36:47 +00:00
|
|
|
func (t *thirdPartyResourceDataCreator) New(groupVersion, kind string) (out runtime.Object, err error) {
|
2015-09-01 05:28:08 +00:00
|
|
|
switch kind {
|
|
|
|
case "ThirdPartyResourceData":
|
2015-09-29 21:36:47 +00:00
|
|
|
if apiutil.GetGroupVersion(t.group, t.version) != groupVersion {
|
|
|
|
return nil, fmt.Errorf("unknown version %s for kind %s", groupVersion, kind)
|
2015-09-18 05:54:15 +00:00
|
|
|
}
|
2015-09-09 22:46:06 +00:00
|
|
|
return &experimental.ThirdPartyResourceData{}, nil
|
2015-09-01 05:28:08 +00:00
|
|
|
case "ThirdPartyResourceDataList":
|
2015-09-29 21:36:47 +00:00
|
|
|
if apiutil.GetGroupVersion(t.group, t.version) != groupVersion {
|
|
|
|
return nil, fmt.Errorf("unknown version %s for kind %s", groupVersion, kind)
|
2015-09-18 05:54:15 +00:00
|
|
|
}
|
2015-09-09 22:46:06 +00:00
|
|
|
return &experimental.ThirdPartyResourceDataList{}, nil
|
2015-09-01 05:28:08 +00:00
|
|
|
default:
|
2015-09-29 21:36:47 +00:00
|
|
|
return t.delegate.New(groupVersion, kind)
|
2015-09-01 05:28:08 +00:00
|
|
|
}
|
|
|
|
}
|