Skip to content

add the crd update for DMP reinstall #851

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

Merged
merged 1 commit into from
Nov 4, 2024
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
10 changes: 10 additions & 0 deletions charts/mysql-operator/templates/cluster_rbac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ metadata:
release: {{ .Release.Name | quote }}
heritage: {{ .Release.Service | quote }}
rules:
- apiGroups:
- apiextensions.k8s.io
resources:
- customresourcedefinitions
verbs:
- get
- list
- patch
- update
- watch
- apiGroups:
- apps
resources:
Expand Down
7 changes: 6 additions & 1 deletion cmd/manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
_ "k8s.io/client-go/plugin/pkg/client/auth"

"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -44,6 +45,7 @@ import (
"github.com/radondb/radondb-mysql-kubernetes/controllers"
"github.com/radondb/radondb-mysql-kubernetes/controllers/backup"
"github.com/radondb/radondb-mysql-kubernetes/internal"
"github.com/radondb/radondb-mysql-kubernetes/utils"
//+kubebuilder:scaffold:imports
)

Expand All @@ -58,6 +60,7 @@ func init() {
utilruntime.Must(mysqlv1alpha1.AddToScheme(scheme))
utilruntime.Must(mysqlv1beta1.AddToScheme(scheme))
//+kubebuilder:scaffold:scheme
utilruntime.Must(apiextensionsv1.AddToScheme(scheme))
}

func main() {
Expand Down Expand Up @@ -171,10 +174,12 @@ func main() {
setupLog.Error(err, "unable to set up ready check")
os.Exit(1)
}

// check crds
utils.RunUpdeteCRD(mgr.GetClient(), &setupLog)
setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
os.Exit(1)
}

}
10 changes: 10 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ metadata:
creationTimestamp: null
name: manager-role
rules:
- apiGroups:
- apiextensions.k8s.io
resources:
- customresourcedefinitions
verbs:
- get
- list
- patch
- update
- watch
- apiGroups:
- apps
resources:
Expand Down
2 changes: 1 addition & 1 deletion config/samples/mysql_v1beta1_mysqlcluster.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ spec:
# path: host

image: percona/percona-server:8.0.25
imagePullPolicy: Always
imagePullPolicy: IfNotPresent
logOpts:
resources:
requests:
Expand Down
1 change: 1 addition & 0 deletions controllers/mysqlcluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type MysqlClusterReconciler struct {
internal.XenonExecutor
}

// +kubebuilder:rbac:groups=apiextensions.k8s.io,resources=customresourcedefinitions,verbs=get;list;watch;update;patch
// +kubebuilder:rbac:groups=mysql.radondb.com,resources=mysqlclusters,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=mysql.radondb.com,resources=mysqlclusters/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=mysql.radondb.com,resources=mysqlclusters/finalizers,verbs=update
Expand Down
140 changes: 140 additions & 0 deletions utils/incluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,20 @@ import (
"os/exec"
"strconv"
"strings"
"time"

"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/remotecommand"
"sigs.k8s.io/controller-runtime/pkg/client"
)

const (
Expand Down Expand Up @@ -207,3 +212,138 @@ func NewConfig() (*rest.Config, error) {
return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
loader, &clientcmd.ConfigOverrides{}).ClientConfig()
}

func UpdateforCRD(crdName string, cli client.Client, log *logr.Logger) error {
// TODO: update CRD

// MYNS=extension-dmp
// CRD1=mysqlclusters.mysql.radondb.com
// CRD2=backups.mysql.radondb.com
// SEC=radondb-mysql-webhook-certs
// CERT=$(kubectl -n $MYNS get secrets $SEC -ojsonpath='{.data.tls\.crt}')
// kubectl patch CustomResourceDefinition $CRD1 --type=merge -p '{"spec":{"conversion":{"webhook":{"clientConfig":{"caBundle":"'$CERT'","service":{"namespace":"'$MYNS'"}}}}}}'
// kubectl patch CustomResourceDefinition $CRD2 --type=merge -p '{"spec":{"conversion":{"webhook":{"clientConfig":{"caBundle":"'$CERT'","service":{"namespace":"'$MYNS'"}}}}}}'
// echo $CERT
// fetch a secret in dmp-extension namespace which is named radondb-mysql-webhook-certs
// 1. first get os environment value MY_NAMESPACE, if not set, use default namespace ,"dmp-extension"
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ns := os.Getenv("MY_NAMESPACE")
if len(ns) == 0 {
ns = "extension-dmp"
}
//2. get os environment value CERT_NAME, if not set, use default namespace "radondb-mysql-webhook-certs"
certName := os.Getenv("CERT_NAME")
if len(certName) == 0 {
certName = "radondb-mysql-webhook-certs"
}
secret := &corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: certName,
Namespace: ns,
},
}
err := cli.Get(ctx, types.NamespacedName{Name: secret.Name, Namespace: secret.Namespace}, secret)
// if err is not found, return error
if errors.IsNotFound(err) {
return fmt.Errorf("secret %s not found", certName)
}
cert := secret.Data["tls.crt"]

//fetch the CustomResourceDefinition ,which name is mysqlclusters.mysql.radondb.com
// 创建 CRD 实例
//apiextensionsv1.AddToScheme(scheme)
//CustomResourceDefinition
crd := &apiextensionsv1.CustomResourceDefinition{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "CustomResourceDefinition",
},
ObjectMeta: metav1.ObjectMeta{
Name: crdName,
},
}
// 使用客户端获取 CRD 资源
errCRD := cli.Get(ctx, types.NamespacedName{Name: crdName}, crd)
if errCRD != nil {
return errCRD
}
hasBetaVersion := false
for _, v := range crd.Spec.Versions {
if v.Name == "v1beta1" {
hasBetaVersion = true
}
}
if !hasBetaVersion {
return fmt.Errorf("has not v1beta1 version")
}

oldCrd := crd.DeepCopy()
// if CustomResourceConversion's CABundle of Webhook is not equal to cert, update it
// sometime the path and port are missing, I don't know why
if oldCrd.Spec.Conversion == nil || oldCrd.Spec.Conversion.Webhook == nil || oldCrd.Spec.Conversion.Webhook.ClientConfig == nil ||
oldCrd.Spec.Conversion.Webhook.ClientConfig.CABundle == nil ||
oldCrd.Spec.Conversion.Webhook.ClientConfig.Service.Path == nil ||
oldCrd.Spec.Conversion.Webhook.ClientConfig.Service.Port == nil ||
!bytes.Equal(oldCrd.Spec.Conversion.Webhook.ClientConfig.CABundle, cert) {
crd.Spec.Conversion = &apiextensionsv1.CustomResourceConversion{
Strategy: apiextensionsv1.WebhookConverter,
Webhook: &apiextensionsv1.WebhookConversion{
ClientConfig: &apiextensionsv1.WebhookClientConfig{
CABundle: []byte(cert),
Service: &apiextensionsv1.ServiceReference{
Namespace: ns,
Name: func() string {
if oldCrd.Spec.Conversion != nil && oldCrd.Spec.Conversion.Webhook != nil && oldCrd.Spec.Conversion.Webhook.ClientConfig != nil {
return oldCrd.Spec.Conversion.Webhook.ClientConfig.Service.Name
} else {
return "radondb-mysql-webhook"
}
}(),
Path: func() *string {
var p string = "/convert"
return &p
}(),
Port: func() *int32 {
var serverPort int32 = 443
return &serverPort
}(),
},
},
ConversionReviewVersions: []string{"v1"},
},
}
log.Info("covert crd", "value", crd.Spec.Conversion)
} else {
return nil
}
errCRD = cli.Patch(ctx, crd, client.MergeFrom(oldCrd))
if errCRD != nil {
return errCRD
}

return nil
}

func RunUpdeteCRD(cli client.Client, log *logr.Logger) {
go func() {
// Just run in the first 500 seconds,almost eight minutes, because the crd webhook's CABundle is not correct just in the DMP reinstall period
// if this process failed, just need to restart the operetor pod
for i := 0; i < 100; i++ {
time.Sleep(time.Second * 5)
err := UpdateforCRD("mysqlclusters.mysql.radondb.com", cli, log)
if err != nil {
log.Info("update CRD failed", "error", err)
}
err = UpdateforCRD("backups.mysql.radondb.com", cli, log)
if err != nil {
log.Info("update CRD failed", "error", err)
}
}
log.Info("check the crd about 8 minutes, now exit.")
}()
}
Loading