Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: support convert configconstraint from v1alpha1 to v1beta1 (#357) #358

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
Loading