diff --git a/Makefile b/Makefile index f5658f5..a7810ce 100644 --- a/Makefile +++ b/Makefile @@ -52,12 +52,13 @@ manifests: setup ## Generate WebhookConfiguration, ClusterRole and CustomResourc kustomize build config/kustomize-to-helm/overlays/templates | yq e "." -p yaml - > charts/accurate/templates/generated/generated.yaml .PHONY: generate -generate: setup generate-applyconfigurations ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. +generate: setup generate-applyconfigurations generate-conversion ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. controller-gen object:headerFile="hack/boilerplate.go.txt" paths="{./api/...}" GO_MODULE = $(shell go list -m) API_DIRS = $(shell find api -mindepth 2 -type d | sed "s|^|$(shell go list -m)/|" | paste -sd " ") AC_PKG = internal/applyconfigurations + .PHONY: generate-applyconfigurations generate-applyconfigurations: setup ## Generate applyconfigurations to support typesafe SSA. @echo ">> generating $(AC_PKG)..." @@ -67,6 +68,13 @@ generate-applyconfigurations: setup ## Generate applyconfigurations to support t --output-pkg "$(GO_MODULE)/$(AC_PKG)" \ $(API_DIRS) +.PHONY: generate-conversion +generate-conversion: setup ## Generate conversion functions to support API conversion. + @echo ">> generating $(AC_PKG)..." + conversion-gen \ + --output-file zz_generated.conversion.go \ + $(API_DIRS) + .PHONY: apidoc apidoc: setup $(wildcard api/*/*_types.go) crd-to-markdown --links docs/links.csv -f api/accurate/v1/subnamespace_types.go -n SubNamespace > docs/crd_subnamespace.md diff --git a/api/accurate/v1/conversion.go b/api/accurate/v1/conversion.go new file mode 100644 index 0000000..e26b336 --- /dev/null +++ b/api/accurate/v1/conversion.go @@ -0,0 +1,79 @@ +package v1 + +import ( + "encoding/json" + "fmt" + "strconv" + + accuratev2alpha1 "github.com/cybozu-go/accurate/api/accurate/v2alpha1" + "github.com/cybozu-go/accurate/pkg/constants" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/conversion" + kstatus "sigs.k8s.io/cli-utils/pkg/kstatus/status" +) + +// Convert_v1_SubNamespace_To_v2alpha1_SubNamespace complements the generated conversion functions since status needs special handling +func Convert_v1_SubNamespace_To_v2alpha1_SubNamespace(in *SubNamespace, out *accuratev2alpha1.SubNamespace, s conversion.Scope) error { + if err := autoConvert_v1_SubNamespace_To_v2alpha1_SubNamespace(in, out, s); err != nil { + return err + } + + // Restore info from annotations to ensure conversions are lossy-less. + // Delete annotation after processing it to avoid polluting converted resource. + if v, ok := out.Annotations[constants.AnnObservedGeneration]; ok { + obsGen, err := strconv.ParseInt(v, 10, 64) + if err != nil { + return fmt.Errorf("error converting %q to int64 from annotation %s", v, constants.AnnObservedGeneration) + } + out.Status.ObservedGeneration = obsGen + + delete(out.Annotations, constants.AnnObservedGeneration) + } + if conds, ok := out.Annotations[constants.AnnConditions]; ok { + err := json.Unmarshal([]byte(conds), &out.Status.Conditions) + if err != nil { + return fmt.Errorf("error unmarshalling JSON from annotation %s", constants.AnnConditions) + } + + delete(out.Annotations, constants.AnnConditions) + } + return nil +} + +// Convert_v2alpha1_SubNamespace_To_v1_SubNamespace complements the generated conversion functions since status needs special handling +func Convert_v2alpha1_SubNamespace_To_v1_SubNamespace(in *accuratev2alpha1.SubNamespace, out *SubNamespace, s conversion.Scope) error { + if err := autoConvert_v2alpha1_SubNamespace_To_v1_SubNamespace(in, out, s); err != nil { + return err + } + + switch { + case meta.IsStatusConditionTrue(in.Status.Conditions, string(kstatus.ConditionStalled)): + out.Status = SubNamespaceConflict + case in.Status.ObservedGeneration == 0: + // SubNamespace has never been reconciled. + case in.Status.ObservedGeneration == in.Generation && len(in.Status.Conditions) == 0: + out.Status = SubNamespaceOK + default: + // SubNamespace is in some transitional state, not possible to represent in v1 status. + // An unset value is probably our best option. + } + + // Store info in annotations to ensure conversions are lossy-less. + if out.Annotations == nil { + out.Annotations = make(map[string]string) + } + if in.Status.ObservedGeneration != 0 { + out.Annotations[constants.AnnObservedGeneration] = strconv.FormatInt(in.Status.ObservedGeneration, 10) + } + if len(in.Status.Conditions) > 0 { + buf, err := json.Marshal(in.Status.Conditions) + if err != nil { + return fmt.Errorf("error marshalling conditions to JSON") + } + out.Annotations[constants.AnnConditions] = string(buf) + } + if len(out.Annotations) == 0 { + out.Annotations = nil + } + return nil +} diff --git a/api/accurate/v1/doc.go b/api/accurate/v1/doc.go index ca9de1b..41fd477 100644 --- a/api/accurate/v1/doc.go +++ b/api/accurate/v1/doc.go @@ -1,3 +1,4 @@ // +kubebuilder:object:generate=true // +groupName=accurate.cybozu.com +// +k8s:conversion-gen=github.com/cybozu-go/accurate/api/accurate/v2alpha1 package v1 diff --git a/api/accurate/v1/groupversion_info.go b/api/accurate/v1/groupversion_info.go index ce2afac..3d1883e 100644 --- a/api/accurate/v1/groupversion_info.go +++ b/api/accurate/v1/groupversion_info.go @@ -15,4 +15,6 @@ var ( // AddToScheme adds the types in this group-version to the given scheme. AddToScheme = SchemeBuilder.AddToScheme + + localSchemeBuilder = SchemeBuilder.SchemeBuilder ) diff --git a/api/accurate/v1/subnamespace_conversion.go b/api/accurate/v1/subnamespace_conversion.go index 9664ca3..2ed5166 100644 --- a/api/accurate/v1/subnamespace_conversion.go +++ b/api/accurate/v1/subnamespace_conversion.go @@ -1,15 +1,8 @@ package v1 import ( - "encoding/json" - "fmt" - "strconv" - accuratev2alpha1 "github.com/cybozu-go/accurate/api/accurate/v2alpha1" - "github.com/cybozu-go/accurate/pkg/constants" "github.com/go-logr/logr" - "k8s.io/apimachinery/pkg/api/meta" - kstatus "sigs.k8s.io/cli-utils/pkg/kstatus/status" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/conversion" @@ -25,31 +18,7 @@ func (src *SubNamespace) ConvertTo(dstRaw conversion.Hub) error { ) logger.V(5).Info("converting") - dst.ObjectMeta = src.ObjectMeta - dst.Spec.Annotations = src.Spec.Annotations - dst.Spec.Labels = src.Spec.Labels - - // Restore info from annotations to ensure conversions are lossy-less. - // Delete annotation after processing it to avoid polluting converted resource. - if v, ok := dst.Annotations[constants.AnnObservedGeneration]; ok { - obsGen, err := strconv.ParseInt(v, 10, 64) - if err != nil { - return fmt.Errorf("error converting %q to int64 from annotation %s", v, constants.AnnObservedGeneration) - } - dst.Status.ObservedGeneration = obsGen - - delete(dst.Annotations, constants.AnnObservedGeneration) - } - if conds, ok := dst.Annotations[constants.AnnConditions]; ok { - err := json.Unmarshal([]byte(conds), &dst.Status.Conditions) - if err != nil { - return fmt.Errorf("error unmarshalling JSON from annotation %s", constants.AnnConditions) - } - - delete(dst.Annotations, constants.AnnConditions) - } - - return nil + return Convert_v1_SubNamespace_To_v2alpha1_SubNamespace(src, dst, nil) } // ConvertFrom converts from the Hub version (v2alpha1) to this version. @@ -62,41 +31,7 @@ func (dst *SubNamespace) ConvertFrom(srcRaw conversion.Hub) error { ) logger.V(5).Info("converting") - dst.ObjectMeta = src.ObjectMeta - dst.Spec.Annotations = src.Spec.Annotations - dst.Spec.Labels = src.Spec.Labels - - switch { - case meta.IsStatusConditionTrue(src.Status.Conditions, string(kstatus.ConditionStalled)): - dst.Status = SubNamespaceConflict - case src.Status.ObservedGeneration == 0: - // SubNamespace has never been reconciled. - case src.Status.ObservedGeneration == src.Generation && len(src.Status.Conditions) == 0: - dst.Status = SubNamespaceOK - default: - // SubNamespace is in some transitional state, not possible to represent in v1 status. - // An unset value is probably our best option. - } - - // Store info in annotations to ensure conversions are lossy-less. - if dst.Annotations == nil { - dst.Annotations = make(map[string]string) - } - if src.Status.ObservedGeneration != 0 { - dst.Annotations[constants.AnnObservedGeneration] = strconv.FormatInt(src.Status.ObservedGeneration, 10) - } - if len(src.Status.Conditions) > 0 { - buf, err := json.Marshal(src.Status.Conditions) - if err != nil { - return fmt.Errorf("error marshalling conditions to JSON") - } - dst.Annotations[constants.AnnConditions] = string(buf) - } - if len(dst.Annotations) == 0 { - dst.Annotations = nil - } - - return nil + return Convert_v2alpha1_SubNamespace_To_v1_SubNamespace(src, dst, nil) } func getConversionLogger(obj client.Object) logr.Logger { diff --git a/api/accurate/v1/zz_generated.conversion.go b/api/accurate/v1/zz_generated.conversion.go new file mode 100644 index 0000000..0586278 --- /dev/null +++ b/api/accurate/v1/zz_generated.conversion.go @@ -0,0 +1,136 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by conversion-gen. DO NOT EDIT. + +package v1 + +import ( + unsafe "unsafe" + + v2alpha1 "github.com/cybozu-go/accurate/api/accurate/v2alpha1" + conversion "k8s.io/apimachinery/pkg/conversion" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +func init() { + localSchemeBuilder.Register(RegisterConversions) +} + +// RegisterConversions adds conversion functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterConversions(s *runtime.Scheme) error { + if err := s.AddGeneratedConversionFunc((*SubNamespaceList)(nil), (*v2alpha1.SubNamespaceList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1_SubNamespaceList_To_v2alpha1_SubNamespaceList(a.(*SubNamespaceList), b.(*v2alpha1.SubNamespaceList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v2alpha1.SubNamespaceList)(nil), (*SubNamespaceList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v2alpha1_SubNamespaceList_To_v1_SubNamespaceList(a.(*v2alpha1.SubNamespaceList), b.(*SubNamespaceList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*SubNamespaceSpec)(nil), (*v2alpha1.SubNamespaceSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1_SubNamespaceSpec_To_v2alpha1_SubNamespaceSpec(a.(*SubNamespaceSpec), b.(*v2alpha1.SubNamespaceSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v2alpha1.SubNamespaceSpec)(nil), (*SubNamespaceSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v2alpha1_SubNamespaceSpec_To_v1_SubNamespaceSpec(a.(*v2alpha1.SubNamespaceSpec), b.(*SubNamespaceSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*SubNamespace)(nil), (*v2alpha1.SubNamespace)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1_SubNamespace_To_v2alpha1_SubNamespace(a.(*SubNamespace), b.(*v2alpha1.SubNamespace), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v2alpha1.SubNamespace)(nil), (*SubNamespace)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v2alpha1_SubNamespace_To_v1_SubNamespace(a.(*v2alpha1.SubNamespace), b.(*SubNamespace), scope) + }); err != nil { + return err + } + return nil +} + +func autoConvert_v1_SubNamespace_To_v2alpha1_SubNamespace(in *SubNamespace, out *v2alpha1.SubNamespace, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1_SubNamespaceSpec_To_v2alpha1_SubNamespaceSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + // WARNING: in.Status requires manual conversion: inconvertible types (github.com/cybozu-go/accurate/api/accurate/v1.SubNamespaceStatus vs github.com/cybozu-go/accurate/api/accurate/v2alpha1.SubNamespaceStatus) + return nil +} + +func autoConvert_v2alpha1_SubNamespace_To_v1_SubNamespace(in *v2alpha1.SubNamespace, out *SubNamespace, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v2alpha1_SubNamespaceSpec_To_v1_SubNamespaceSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + // WARNING: in.Status requires manual conversion: inconvertible types (github.com/cybozu-go/accurate/api/accurate/v2alpha1.SubNamespaceStatus vs github.com/cybozu-go/accurate/api/accurate/v1.SubNamespaceStatus) + return nil +} + +func autoConvert_v1_SubNamespaceList_To_v2alpha1_SubNamespaceList(in *SubNamespaceList, out *v2alpha1.SubNamespaceList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v2alpha1.SubNamespace, len(*in)) + for i := range *in { + if err := Convert_v1_SubNamespace_To_v2alpha1_SubNamespace(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1_SubNamespaceList_To_v2alpha1_SubNamespaceList is an autogenerated conversion function. +func Convert_v1_SubNamespaceList_To_v2alpha1_SubNamespaceList(in *SubNamespaceList, out *v2alpha1.SubNamespaceList, s conversion.Scope) error { + return autoConvert_v1_SubNamespaceList_To_v2alpha1_SubNamespaceList(in, out, s) +} + +func autoConvert_v2alpha1_SubNamespaceList_To_v1_SubNamespaceList(in *v2alpha1.SubNamespaceList, out *SubNamespaceList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]SubNamespace, len(*in)) + for i := range *in { + if err := Convert_v2alpha1_SubNamespace_To_v1_SubNamespace(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v2alpha1_SubNamespaceList_To_v1_SubNamespaceList is an autogenerated conversion function. +func Convert_v2alpha1_SubNamespaceList_To_v1_SubNamespaceList(in *v2alpha1.SubNamespaceList, out *SubNamespaceList, s conversion.Scope) error { + return autoConvert_v2alpha1_SubNamespaceList_To_v1_SubNamespaceList(in, out, s) +} + +func autoConvert_v1_SubNamespaceSpec_To_v2alpha1_SubNamespaceSpec(in *SubNamespaceSpec, out *v2alpha1.SubNamespaceSpec, s conversion.Scope) error { + out.Labels = *(*map[string]string)(unsafe.Pointer(&in.Labels)) + out.Annotations = *(*map[string]string)(unsafe.Pointer(&in.Annotations)) + return nil +} + +// Convert_v1_SubNamespaceSpec_To_v2alpha1_SubNamespaceSpec is an autogenerated conversion function. +func Convert_v1_SubNamespaceSpec_To_v2alpha1_SubNamespaceSpec(in *SubNamespaceSpec, out *v2alpha1.SubNamespaceSpec, s conversion.Scope) error { + return autoConvert_v1_SubNamespaceSpec_To_v2alpha1_SubNamespaceSpec(in, out, s) +} + +func autoConvert_v2alpha1_SubNamespaceSpec_To_v1_SubNamespaceSpec(in *v2alpha1.SubNamespaceSpec, out *SubNamespaceSpec, s conversion.Scope) error { + out.Labels = *(*map[string]string)(unsafe.Pointer(&in.Labels)) + out.Annotations = *(*map[string]string)(unsafe.Pointer(&in.Annotations)) + return nil +} + +// Convert_v2alpha1_SubNamespaceSpec_To_v1_SubNamespaceSpec is an autogenerated conversion function. +func Convert_v2alpha1_SubNamespaceSpec_To_v1_SubNamespaceSpec(in *v2alpha1.SubNamespaceSpec, out *SubNamespaceSpec, s conversion.Scope) error { + return autoConvert_v2alpha1_SubNamespaceSpec_To_v1_SubNamespaceSpec(in, out, s) +} diff --git a/aqua.yaml b/aqua.yaml index b13de39..cb41837 100644 --- a/aqua.yaml +++ b/aqua.yaml @@ -20,3 +20,5 @@ packages: - name: goreleaser/goreleaser@v1.19.1 - name: kubernetes/code-generator/applyconfiguration-gen@v0.30.0 registry: local + - name: kubernetes/code-generator/conversion-gen@v0.30.0 + registry: local diff --git a/registry.yaml b/registry.yaml index 62206e4..84c156b 100644 --- a/registry.yaml +++ b/registry.yaml @@ -7,3 +7,11 @@ packages: description: applyconfiguration-gen is a tool for auto-generating apply configuration functions. files: - name: applyconfiguration-gen + - type: go_install + name: kubernetes/code-generator/conversion-gen + path: k8s.io/code-generator/cmd/conversion-gen + repo_owner: kubernetes + repo_name: code-generator + description: conversion-gen is a tool for auto-generating API conversion functions. + files: + - name: conversion-gen