Skip to content

Commit

Permalink
chore: support convert configconstraint from v1alpha1 to v1beta1 (#358)
Browse files Browse the repository at this point in the history
(cherry picked from commit 1b5e79a)
  • Loading branch information
sophon-zt committed May 20, 2024
1 parent 43d905b commit 504b0b2
Show file tree
Hide file tree
Showing 6 changed files with 245 additions and 5 deletions.
20 changes: 20 additions & 0 deletions pkg/cmd/kubeblocks/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
appsv1 "k8s.io/api/apps/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
apitypes "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/cli-runtime/pkg/genericiooptions"
Expand All @@ -44,6 +45,7 @@ import (
"github.com/apecloud/kbcli/pkg/types"
"github.com/apecloud/kbcli/pkg/util"
"github.com/apecloud/kbcli/pkg/util/breakingchange"
"github.com/apecloud/kbcli/pkg/util/conversion"
"github.com/apecloud/kbcli/pkg/util/helm"
"github.com/apecloud/kbcli/pkg/util/prompt"
)
Expand Down Expand Up @@ -205,6 +207,16 @@ func (o *InstallOptions) Upgrade() error {
return err
}

// save old version crs
conversionMeta := conversion.NewVersionConversion(o.Dynamic, o.OldVersion, o.Version)
s = spinner.New(o.Out, spinnerMsg("Conversion old version[%s] CRs to new version[%s]", o.OldVersion, o.Version))
defer s.Fail()
var unstructuredObjects []unstructured.Unstructured
if unstructuredObjects, err = conversion.FetchAndConversionResources(conversionMeta); err != nil {
return fmt.Errorf("conversion crs failed: %s", err.Error())
}
s.Success()

// create or update crds
s = spinner.New(o.Out, spinnerMsg("Upgrade CRDs"))
defer s.Fail()
Expand All @@ -213,6 +225,14 @@ func (o *InstallOptions) Upgrade() error {
}
s.Success()

// conversion new version crs
s = spinner.New(o.Out, spinnerMsg("update new version CRs"))
defer s.Fail()
if err = conversion.UpdateNewVersionResources(conversionMeta, unstructuredObjects); err != nil {
return fmt.Errorf("update new crs failed: %s", err.Error())
}
s.Success()

s = spinner.New(o.Out, spinnerMsg("Upgrading KubeBlocks "+msg))
defer s.Fail()
// upgrade KubeBlocks chart
Expand Down
4 changes: 4 additions & 0 deletions pkg/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,10 @@ func ConfigConstraintGVR() schema.GroupVersionResource {
return schema.GroupVersionResource{Group: AppsAPIGroup, Version: AppsAPIBetaVersion, Resource: ResourceConfigConstraintVersions}
}

func ConfigConstraintOldGVR() schema.GroupVersionResource {
return schema.GroupVersionResource{Group: AppsAPIGroup, Version: AppsAPIVersion, Resource: ResourceConfigConstraintVersions}
}

func StorageClassGVR() schema.GroupVersionResource {
return schema.GroupVersionResource{
Group: "storage.k8s.io",
Expand Down
10 changes: 5 additions & 5 deletions pkg/util/breakingchange/upgrader.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,14 @@ func registerUpgradeHandler(fromVersions []string, toVersion string, upgradeHand

var majorMinorFromVersions []string
for _, v := range fromVersions {
majorMinorFromVersion := getMajorMinorVersion(v)
majorMinorFromVersion := GetMajorMinorVersion(v)
if majorMinorFromVersion == "" {
panic(formatErr(v))
}
majorMinorFromVersions = append(majorMinorFromVersions, majorMinorFromVersion)
}

majorMinorToVersion := getMajorMinorVersion(toVersion)
majorMinorToVersion := GetMajorMinorVersion(toVersion)
if majorMinorToVersion == "" {
panic(formatErr(toVersion))
}
Expand All @@ -80,8 +80,8 @@ func registerUpgradeHandler(fromVersions []string, toVersion string, upgradeHand

// getUpgradeHandler gets the upgrade handler according to fromVersion and toVersion from upgradeHandlerMapper.
func getUpgradeHandler(fromVersion, toVersion string) upgradeHandler {
majorMinorFromVersion := getMajorMinorVersion(fromVersion)
majorMinorToVersion := getMajorMinorVersion(toVersion)
majorMinorFromVersion := GetMajorMinorVersion(fromVersion)
majorMinorToVersion := GetMajorMinorVersion(toVersion)
handlerRecorder, ok := upgradeHandlerMapper[majorMinorToVersion]
if !ok {
return nil
Expand Down Expand Up @@ -123,7 +123,7 @@ func ValidateUpgradeVersion(fromVersion, toVersion string) error {
return nil
}

func getMajorMinorVersion(version string) string {
func GetMajorMinorVersion(version string) string {
vs := strings.Split(version, ".")
if len(vs) < 2 {
return ""
Expand Down
133 changes: 133 additions & 0 deletions pkg/util/conversion/conversion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
Copyright (C) 2022-2024 ApeCloud Co., Ltd
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 conversion

import (
"fmt"

appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1"
appsv1beta1 "github.com/apecloud/kubeblocks/apis/apps/v1beta1"
"github.com/apecloud/kubeblocks/pkg/constant"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
apiruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/dynamic"
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/apecloud/kbcli/pkg/types"
"github.com/apecloud/kbcli/pkg/util"
)

const (
OldVersion = "08"
NewVersion = "09"
)

func FetchAndConversionResources(versionMeta *VersionConversionMeta) ([]unstructured.Unstructured, error) {
var resources []unstructured.Unstructured

if versionMeta.FromVersion == versionMeta.ToVersion {
return nil, nil
}

if versionMeta.FromVersion != OldVersion || versionMeta.ToVersion != NewVersion {
klog.V(1).Infof("not to convert configconstraint multiversion")
return nil, nil
}

oldResources, err := ResourcesWithGVR(versionMeta, types.ConfigConstraintOldGVR(), metav1.ListOptions{})
if err != nil {
return nil, err
}
for _, oldObj := range oldResources {
newObj := appsv1beta1.ConfigConstraint{
TypeMeta: metav1.TypeMeta{
Kind: types.KindConfigConstraint,
APIVersion: types.ConfigConstraintGVR().GroupVersion().String(),
},
}
klog.V(1).Infof("convert configconstraint[%s] cr from v1alpha1 to v1beta1", oldObj.GetName())
// If v1alpha1 is converted from v1beta1 version
if hasConversionVersion(&oldObj) {
klog.V(1).Infof("configconstraint[%s] v1alpha1 is converted from v1beta1 version and ignore.",
client.ObjectKeyFromObject(&oldObj).String())
continue
}
// If the converted version v1beta1 already exists
if hasValidBetaVersion(&oldObj, versionMeta) {
klog.V(1).Infof("configconstraint[%s] v1beta1 already exist and ignore.",
client.ObjectKeyFromObject(&oldObj).String())
continue
}
if err := oldObj.ConvertTo(&newObj); err != nil {
return nil, err
}
item, err := apiruntime.DefaultUnstructuredConverter.ToUnstructured(&newObj)
if err != nil {
return nil, err
}
resources = append(resources, unstructured.Unstructured{Object: item})
}
return resources, nil
}

func hasValidBetaVersion(obj *appsv1alpha1.ConfigConstraint, dynamic dynamic.Interface) bool {
newObj := appsv1beta1.ConfigConstraint{}
if err := util.GetResourceObjectFromGVR(types.ConfigConstraintGVR(), client.ObjectKeyFromObject(obj), dynamic, &newObj); err != nil {
return false
}

return hasConversionVersion(&newObj)
}

func hasConversionVersion(obj client.Object) bool {
annotations := obj.GetAnnotations()
if len(annotations) == 0 {
return false
}
return annotations[constant.KubeblocksAPIConversionTypeAnnotationName] == constant.MigratedAPIVersion
}

func UpdateNewVersionResources(versionMeta *VersionConversionMeta, targetObjects []unstructured.Unstructured) error {
if len(targetObjects) == 0 {
return nil
}
if versionMeta.FromVersion == versionMeta.ToVersion {
return nil
}

for _, unstructuredObj := range targetObjects {
klog.V(1).Infof("update CR %s", unstructuredObj.GetName())
_, err := versionMeta.Resource(types.ConfigConstraintGVR()).
Update(versionMeta.Ctx, &unstructuredObj, metav1.UpdateOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
if _, err := versionMeta.Resource(types.ConfigConstraintGVR()).
Create(versionMeta.Ctx, &unstructuredObj, metav1.CreateOptions{}); err != nil {
klog.V(1).ErrorS(err, "failed to create configConstraint")
return err
}
continue
}
klog.V(1).ErrorS(err, fmt.Sprintf("failed to update configconstraint cr[%v]", unstructuredObj.GetName()))
return err
}
}
return nil
}
42 changes: 42 additions & 0 deletions pkg/util/conversion/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
Copyright (C) 2022-2024 ApeCloud Co., Ltd
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 conversion

import (
"context"

"k8s.io/client-go/dynamic"

"github.com/apecloud/kbcli/pkg/util/breakingchange"
)

type VersionConversionMeta struct {
dynamic.Interface
Ctx context.Context

FromVersion string
ToVersion string
}

func NewVersionConversion(dynamic dynamic.Interface, fromVersion, toVersion string) *VersionConversionMeta {
return &VersionConversionMeta{
Ctx: context.Background(),
Interface: dynamic,
FromVersion: breakingchange.GetMajorMinorVersion(fromVersion),
ToVersion: breakingchange.GetMajorMinorVersion(toVersion),
}
}
41 changes: 41 additions & 0 deletions pkg/util/conversion/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
Copyright (C) 2022-2024 ApeCloud Co., Ltd
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 conversion

import (
appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
apiruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)

func ResourcesWithGVR(versionMeta *VersionConversionMeta, gvr schema.GroupVersionResource, listOptions metav1.ListOptions) ([]appsv1alpha1.ConfigConstraint, error) {
var resourcesList []appsv1alpha1.ConfigConstraint

objList, err := versionMeta.Resource(gvr).List(versionMeta.Ctx, listOptions)
if err != nil {
return nil, err
}
for _, v := range objList.Items {
obj := appsv1alpha1.ConfigConstraint{}
if err := apiruntime.DefaultUnstructuredConverter.FromUnstructured(v.Object, &obj); err != nil {
return nil, err
}
resourcesList = append(resourcesList, obj)
}
return resourcesList, nil
}

0 comments on commit 504b0b2

Please sign in to comment.