Skip to content

Commit

Permalink
crd implementation w/ test
Browse files Browse the repository at this point in the history
  • Loading branch information
ublubu committed Dec 20, 2017
1 parent 68e1995 commit 72f9015
Show file tree
Hide file tree
Showing 8 changed files with 415 additions and 0 deletions.
145 changes: 145 additions & 0 deletions converter/converters/koki_crd_to_kube.go
@@ -0,0 +1,145 @@
package converters

import (
apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"

"github.com/koki/short/types"
serrors "github.com/koki/structurederrors"
)

func Convert_Koki_CRD_to_Kube(kokiWrapper *types.CRDWrapper) (*apiext.CustomResourceDefinition, error) {
var err error
kube := &apiext.CustomResourceDefinition{}
koki := kokiWrapper.CRD

kube.Name = koki.Name
kube.Namespace = koki.Namespace
if len(koki.Version) == 0 {
kube.APIVersion = "apiextensions/v1beta1"
} else {
kube.APIVersion = koki.Version
}
kube.Kind = "CustomResourceDefinition"
kube.ClusterName = koki.Cluster
kube.Labels = koki.Labels
kube.Annotations = koki.Annotations

kubeSpec := &kube.Spec
kubeSpec.Group = koki.CRDMeta.Group
kubeSpec.Version = koki.CRDMeta.Version
kubeSpec.Names = revertCRDNames(koki.CRDMeta.CRDName)
if err != nil {
return nil, serrors.ContextualizeErrorf(err, "CRD names")
}
kubeSpec.Scope, err = revertCRDScope(koki.Scope)
if err != nil {
return nil, serrors.ContextualizeErrorf(err, "CRD resource scope")
}
kubeSpec.Validation = revertCRDValidation(koki.Validation)

kube.Status.Conditions, err = revertCRDConditions(koki.Conditions)
if err != nil {
return nil, serrors.ContextualizeErrorf(err, "CRD conditions")
}
kube.Status.AcceptedNames = revertCRDNames(koki.Accepted)

return kube, nil
}

func revertCRDNames(koki types.CRDName) apiext.CustomResourceDefinitionNames {
return apiext.CustomResourceDefinitionNames{
Plural: koki.Plural,
Singular: koki.Singular,
ShortNames: koki.ShortNames,
Kind: koki.Kind,
ListKind: koki.ListKind,
}
}

func revertCRDScope(koki types.CRDResourceScope) (apiext.ResourceScope, error) {
switch koki {
case "":
return "", nil
case types.CRDClusterScoped:
return apiext.ClusterScoped, nil
case types.CRDNamespaceScoped:
return apiext.NamespaceScoped, nil
default:
return "", serrors.InvalidInstanceError(koki)
}
}

func revertCRDValidation(koki *apiext.JSONSchemaProps) *apiext.CustomResourceValidation {
if koki == nil {
return nil
}

return &apiext.CustomResourceValidation{
OpenAPIV3Schema: koki,
}
}

func revertCRDConditions(kokis []types.CRDCondition) ([]apiext.CustomResourceDefinitionCondition, error) {
if len(kokis) == 0 {
return nil, nil
}

var err error
kubes := make([]apiext.CustomResourceDefinitionCondition, len(kokis))
for i, koki := range kokis {
kubes[i], err = revertCRDCondition(koki)
if err != nil {
return nil, serrors.ContextualizeErrorf(err, "[%d]", i)
}
}

return kubes, nil
}

func revertCRDCondition(koki types.CRDCondition) (apiext.CustomResourceDefinitionCondition, error) {
var err error
condition := apiext.CustomResourceDefinitionCondition{
LastTransitionTime: koki.LastTransitionTime,
Reason: koki.Reason,
Message: koki.Message,
}
condition.Type, err = revertCRDConditionType(koki.Type)
if err != nil {
return condition, serrors.ContextualizeErrorf(err, "type")
}
condition.Status, err = revertCRDConditionStatus(koki.Status)
if err != nil {
return condition, serrors.ContextualizeErrorf(err, "status")
}

return condition, nil
}

func revertCRDConditionType(koki types.CRDConditionType) (apiext.CustomResourceDefinitionConditionType, error) {
switch koki {
case types.CRDEstablished:
return apiext.Established, nil
case types.CRDNamesAccepted:
return apiext.NamesAccepted, nil
case types.CRDTerminating:
return apiext.Terminating, nil
default:
return "", serrors.InvalidInstanceError(koki)
}
}

func revertCRDConditionStatus(status types.ConditionStatus) (apiext.ConditionStatus, error) {
if status == "" {
return "", nil
}
if status == types.ConditionTrue {
return apiext.ConditionTrue, nil
}
if status == types.ConditionFalse {
return apiext.ConditionFalse, nil
}
if status == types.ConditionUnknown {
return apiext.ConditionUnknown, nil
}
return "", serrors.InvalidInstanceError(status)
}
143 changes: 143 additions & 0 deletions converter/converters/kube_crd_to_koki.go
@@ -0,0 +1,143 @@
package converters

import (
apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"

"github.com/koki/short/types"
serrors "github.com/koki/structurederrors"
)

func Convert_Kube_CRD_to_Koki(kube *apiext.CustomResourceDefinition) (*types.CRDWrapper, error) {
var err error
koki := &types.CustomResourceDefinition{}

koki.Name = kube.Name
koki.Namespace = kube.Namespace
koki.Version = kube.APIVersion
koki.Cluster = kube.ClusterName
koki.Labels = kube.Labels
koki.Annotations = kube.Annotations

koki.CRDMeta = convertCRDMeta(kube.Spec)
koki.Scope, err = convertCRDScope(kube.Spec.Scope)
if err != nil {
return nil, serrors.ContextualizeErrorf(err, "CRD resource scope")
}
koki.Validation = convertCRDValidation(kube.Spec.Validation)

koki.Conditions, err = convertCRDConditions(kube.Status.Conditions)
if err != nil {
return nil, serrors.ContextualizeErrorf(err, "CRD conditions")
}
koki.Accepted = convertCRDNames(kube.Status.AcceptedNames)

return &types.CRDWrapper{
CRD: *koki,
}, nil
}

func convertCRDMeta(kube apiext.CustomResourceDefinitionSpec) types.CRDMeta {
meta := types.CRDMeta{
Group: kube.Group,
Version: kube.Version,
CRDName: convertCRDNames(kube.Names),
}

return meta
}

func convertCRDNames(kube apiext.CustomResourceDefinitionNames) types.CRDName {
return types.CRDName{
Plural: kube.Plural,
Singular: kube.Singular,
ShortNames: kube.ShortNames,
Kind: kube.Kind,
ListKind: kube.ListKind,
}
}

func convertCRDScope(kube apiext.ResourceScope) (types.CRDResourceScope, error) {
switch kube {
case "":
return "", nil
case apiext.ClusterScoped:
return types.CRDClusterScoped, nil
case apiext.NamespaceScoped:
return types.CRDNamespaceScoped, nil
default:
return "", serrors.InvalidInstanceError(kube)
}
}

func convertCRDValidation(kube *apiext.CustomResourceValidation) *apiext.JSONSchemaProps {
if kube == nil {
return nil
}

return kube.OpenAPIV3Schema
}

func convertCRDConditions(kubes []apiext.CustomResourceDefinitionCondition) ([]types.CRDCondition, error) {
if len(kubes) == 0 {
return nil, nil
}

var err error
kokis := make([]types.CRDCondition, len(kubes))
for i, kube := range kubes {
kokis[i], err = convertCRDCondition(kube)
if err != nil {
return nil, serrors.ContextualizeErrorf(err, "[%d]", i)
}
}

return kokis, nil
}

func convertCRDCondition(kube apiext.CustomResourceDefinitionCondition) (types.CRDCondition, error) {
var err error
condition := types.CRDCondition{
LastTransitionTime: kube.LastTransitionTime,
Reason: kube.Reason,
Message: kube.Message,
}
condition.Type, err = convertCRDConditionType(kube.Type)
if err != nil {
return condition, serrors.ContextualizeErrorf(err, "type")
}
condition.Status, err = convertCRDConditionStatus(kube.Status)
if err != nil {
return condition, serrors.ContextualizeErrorf(err, "status")
}

return condition, nil
}

func convertCRDConditionType(kube apiext.CustomResourceDefinitionConditionType) (types.CRDConditionType, error) {
switch kube {
case apiext.Established:
return types.CRDEstablished, nil
case apiext.NamesAccepted:
return types.CRDNamesAccepted, nil
case apiext.Terminating:
return types.CRDTerminating, nil
default:
return "", serrors.InvalidInstanceError(kube)
}
}

func convertCRDConditionStatus(status apiext.ConditionStatus) (types.ConditionStatus, error) {
if status == "" {
return "", nil
}
if status == apiext.ConditionTrue {
return types.ConditionTrue, nil
}
if status == apiext.ConditionFalse {
return types.ConditionFalse, nil
}
if status == apiext.ConditionUnknown {
return types.ConditionUnknown, nil
}
return "", serrors.InvalidInstanceError(status)
}
5 changes: 5 additions & 0 deletions converter/koki_converter.go
Expand Up @@ -15,6 +15,7 @@ import (
exts "k8s.io/api/extensions/v1beta1"
storagev1 "k8s.io/api/storage/v1"
storagev1beta1 "k8s.io/api/storage/v1beta1"
apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
)

Expand All @@ -26,6 +27,8 @@ func DetectAndConvertFromKokiObj(kokiObj interface{}) (interface{}, error) {
return converters.Convert_Koki_ControllerRevision_to_Kube(kokiObj)
case *types.CronJobWrapper:
return converters.Convert_Koki_CronJob_to_Kube_CronJob(kokiObj)
case *types.CRDWrapper:
return converters.Convert_Koki_CRD_to_Kube(kokiObj)
case *types.DaemonSetWrapper:
return converters.Convert_Koki_DaemonSet_to_Kube_DaemonSet(kokiObj)
case *types.DeploymentWrapper:
Expand Down Expand Up @@ -69,6 +72,8 @@ func DetectAndConvertFromKubeObj(kubeObj runtime.Object) (interface{}, error) {
return converters.Convert_Kube_ControllerRevision_to_Koki(kubeObj)
case *batchv1beta1.CronJob, *batchv2alpha1.CronJob:
return converters.Convert_Kube_CronJob_to_Koki_CronJob(kubeObj)
case *apiext.CustomResourceDefinition:
return converters.Convert_Kube_CRD_to_Koki(kubeObj)
case *appsv1beta2.DaemonSet, *exts.DaemonSet:
return converters.Convert_Kube_DaemonSet_to_Koki_DaemonSet(kubeObj)
case *appsv1beta1.Deployment, *appsv1beta2.Deployment, *exts.Deployment:
Expand Down
2 changes: 2 additions & 0 deletions parser/kube_creator.go
Expand Up @@ -29,6 +29,7 @@ import (
settingsv1alpha1 "k8s.io/api/settings/v1alpha1"
storagev1 "k8s.io/api/storage/v1"
storagev1beta1 "k8s.io/api/storage/v1beta1"
apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
schema "k8s.io/apimachinery/pkg/runtime/schema"
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
)
Expand All @@ -47,6 +48,7 @@ func init() {

func AddToScheme(scheme *runtime.Scheme) {
admissionregistrationv1alpha1.AddToScheme(scheme)
apiext.AddToScheme(scheme)
appsv1beta1.AddToScheme(scheme)
appsv1beta2.AddToScheme(scheme)
appsv1.AddToScheme(scheme)
Expand Down
7 changes: 7 additions & 0 deletions parser/native.go
Expand Up @@ -39,6 +39,13 @@ func ParseKokiNativeObject(obj interface{}) (interface{}, error) {
return nil, serrors.InvalidValueForTypeContextError(err, objMap, rev)
}
return rev, nil
case "crd":
result := &types.CRDWrapper{}
err := json.Unmarshal(bytes, result)
if err != nil {
return nil, serrors.InvalidValueForTypeContextError(err, objMap, result)
}
return result, nil
case "cron_job":
cronJob := &types.CronJobWrapper{}
err := json.Unmarshal(bytes, cronJob)
Expand Down
15 changes: 15 additions & 0 deletions testdata/crds/crd.short.yaml
@@ -0,0 +1,15 @@
crd:
accepted: {}
meta:
group: stable.example.com
kind: CronTab
plural: crontabs
short:
- ct
singular: crontab
version: v1
name: crontabs.stable.example.com
scope: ns
validation: null
version: apiextensions.k8s.io/v1beta1

22 changes: 22 additions & 0 deletions testdata/crds/crd.yaml
@@ -0,0 +1,22 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
# name must match the spec fields below, and be in the form: <plural>.<group>
name: crontabs.stable.example.com
spec:
# group name to use for REST API: /apis/<group>/<version>
group: stable.example.com
# version name to use for REST API: /apis/<group>/<version>
version: v1
# either Namespaced or Cluster
scope: Namespaced
names:
# plural name to be used in the URL: /apis/<group>/<version>/<plural>
plural: crontabs
# singular name to be used as an alias on the CLI and for display
singular: crontab
# kind is normally the CamelCased singular type. Your resource manifests use this.
kind: CronTab
# shortNames allow shorter string to match your resource on the CLI
shortNames:
- ct

0 comments on commit 72f9015

Please sign in to comment.