Skip to content

Commit 7f9caf7

Browse files
committed
add the crd update for DMP reinstall
1 parent 72ee68c commit 7f9caf7

File tree

6 files changed

+168
-2
lines changed

6 files changed

+168
-2
lines changed

charts/mysql-operator/templates/cluster_rbac.yaml

+10
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@ metadata:
99
release: {{ .Release.Name | quote }}
1010
heritage: {{ .Release.Service | quote }}
1111
rules:
12+
- apiGroups:
13+
- apiextensions.k8s.io
14+
resources:
15+
- customresourcedefinitions
16+
verbs:
17+
- get
18+
- list
19+
- patch
20+
- update
21+
- watch
1222
- apiGroups:
1323
- apps
1424
resources:

cmd/manager/main.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424

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

2930
"k8s.io/apimachinery/pkg/runtime"
@@ -44,6 +45,7 @@ import (
4445
"github.com/radondb/radondb-mysql-kubernetes/controllers"
4546
"github.com/radondb/radondb-mysql-kubernetes/controllers/backup"
4647
"github.com/radondb/radondb-mysql-kubernetes/internal"
48+
"github.com/radondb/radondb-mysql-kubernetes/utils"
4749
//+kubebuilder:scaffold:imports
4850
)
4951

@@ -58,6 +60,7 @@ func init() {
5860
utilruntime.Must(mysqlv1alpha1.AddToScheme(scheme))
5961
utilruntime.Must(mysqlv1beta1.AddToScheme(scheme))
6062
//+kubebuilder:scaffold:scheme
63+
utilruntime.Must(apiextensionsv1.AddToScheme(scheme))
6164
}
6265

6366
func main() {
@@ -171,10 +174,12 @@ func main() {
171174
setupLog.Error(err, "unable to set up ready check")
172175
os.Exit(1)
173176
}
174-
177+
// check crds
178+
utils.RunUpdeteCRD(mgr.GetClient(), &setupLog)
175179
setupLog.Info("starting manager")
176180
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
177181
setupLog.Error(err, "problem running manager")
178182
os.Exit(1)
179183
}
184+
180185
}

config/rbac/role.yaml

+10
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ metadata:
66
creationTimestamp: null
77
name: manager-role
88
rules:
9+
- apiGroups:
10+
- apiextensions.k8s.io
11+
resources:
12+
- customresourcedefinitions
13+
verbs:
14+
- get
15+
- list
16+
- patch
17+
- update
18+
- watch
919
- apiGroups:
1020
- apps
1121
resources:

config/samples/mysql_v1beta1_mysqlcluster.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ spec:
2424
# path: host
2525

2626
image: percona/percona-server:8.0.25
27-
imagePullPolicy: Always
27+
imagePullPolicy: IfNotPresent
2828
logOpts:
2929
resources:
3030
requests:

controllers/mysqlcluster_controller.go

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ type MysqlClusterReconciler struct {
5555
internal.XenonExecutor
5656
}
5757

58+
// +kubebuilder:rbac:groups=apiextensions.k8s.io,resources=customresourcedefinitions,verbs=get;list;watch;update;patch
5859
// +kubebuilder:rbac:groups=mysql.radondb.com,resources=mysqlclusters,verbs=get;list;watch;create;update;patch;delete
5960
// +kubebuilder:rbac:groups=mysql.radondb.com,resources=mysqlclusters/status,verbs=get;update;patch
6061
// +kubebuilder:rbac:groups=mysql.radondb.com,resources=mysqlclusters/finalizers,verbs=update

utils/incluster.go

+140
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,20 @@ import (
2828
"os/exec"
2929
"strconv"
3030
"strings"
31+
"time"
3132

33+
"github.com/go-logr/logr"
3234
corev1 "k8s.io/api/core/v1"
35+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
36+
"k8s.io/apimachinery/pkg/api/errors"
3337
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3438
"k8s.io/apimachinery/pkg/runtime"
3539
"k8s.io/apimachinery/pkg/types"
3640
"k8s.io/client-go/kubernetes"
3741
"k8s.io/client-go/rest"
3842
"k8s.io/client-go/tools/clientcmd"
3943
"k8s.io/client-go/tools/remotecommand"
44+
"sigs.k8s.io/controller-runtime/pkg/client"
4045
)
4146

4247
const (
@@ -207,3 +212,138 @@ func NewConfig() (*rest.Config, error) {
207212
return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
208213
loader, &clientcmd.ConfigOverrides{}).ClientConfig()
209214
}
215+
216+
func UpdateforCRD(crdName string, cli client.Client, log *logr.Logger) error {
217+
// TODO: update CRD
218+
219+
// MYNS=extension-dmp
220+
// CRD1=mysqlclusters.mysql.radondb.com
221+
// CRD2=backups.mysql.radondb.com
222+
// SEC=radondb-mysql-webhook-certs
223+
// CERT=$(kubectl -n $MYNS get secrets $SEC -ojsonpath='{.data.tls\.crt}')
224+
// kubectl patch CustomResourceDefinition $CRD1 --type=merge -p '{"spec":{"conversion":{"webhook":{"clientConfig":{"caBundle":"'$CERT'","service":{"namespace":"'$MYNS'"}}}}}}'
225+
// kubectl patch CustomResourceDefinition $CRD2 --type=merge -p '{"spec":{"conversion":{"webhook":{"clientConfig":{"caBundle":"'$CERT'","service":{"namespace":"'$MYNS'"}}}}}}'
226+
// echo $CERT
227+
// fetch a secret in dmp-extension namespace which is named radondb-mysql-webhook-certs
228+
// 1. first get os environment value MY_NAMESPACE, if not set, use default namespace ,"dmp-extension"
229+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
230+
defer cancel()
231+
ns := os.Getenv("MY_NAMESPACE")
232+
if len(ns) == 0 {
233+
ns = "extension-dmp"
234+
}
235+
//2. get os environment value CERT_NAME, if not set, use default namespace "radondb-mysql-webhook-certs"
236+
certName := os.Getenv("CERT_NAME")
237+
if len(certName) == 0 {
238+
certName = "radondb-mysql-webhook-certs"
239+
}
240+
secret := &corev1.Secret{
241+
TypeMeta: metav1.TypeMeta{
242+
Kind: "Secret",
243+
APIVersion: "v1",
244+
},
245+
ObjectMeta: metav1.ObjectMeta{
246+
Name: certName,
247+
Namespace: ns,
248+
},
249+
}
250+
err := cli.Get(ctx, types.NamespacedName{Name: secret.Name, Namespace: secret.Namespace}, secret)
251+
// if err is not found, return error
252+
if errors.IsNotFound(err) {
253+
return fmt.Errorf("secret %s not found", certName)
254+
}
255+
cert := secret.Data["tls.crt"]
256+
257+
//fetch the CustomResourceDefinition ,which name is mysqlclusters.mysql.radondb.com
258+
// 创建 CRD 实例
259+
//apiextensionsv1.AddToScheme(scheme)
260+
//CustomResourceDefinition
261+
crd := &apiextensionsv1.CustomResourceDefinition{
262+
TypeMeta: metav1.TypeMeta{
263+
APIVersion: "v1",
264+
Kind: "CustomResourceDefinition",
265+
},
266+
ObjectMeta: metav1.ObjectMeta{
267+
Name: crdName,
268+
},
269+
}
270+
// 使用客户端获取 CRD 资源
271+
errCRD := cli.Get(ctx, types.NamespacedName{Name: crdName}, crd)
272+
if errCRD != nil {
273+
return errCRD
274+
}
275+
hasBetaVersion := false
276+
for _, v := range crd.Spec.Versions {
277+
if v.Name == "v1beta1" {
278+
hasBetaVersion = true
279+
}
280+
}
281+
if !hasBetaVersion {
282+
return fmt.Errorf("has not v1beta1 version")
283+
}
284+
285+
oldCrd := crd.DeepCopy()
286+
// if CustomResourceConversion's CABundle of Webhook is not equal to cert, update it
287+
// sometime the path and port are missing, I don't know why
288+
if oldCrd.Spec.Conversion == nil || oldCrd.Spec.Conversion.Webhook == nil || oldCrd.Spec.Conversion.Webhook.ClientConfig == nil ||
289+
oldCrd.Spec.Conversion.Webhook.ClientConfig.CABundle == nil ||
290+
oldCrd.Spec.Conversion.Webhook.ClientConfig.Service.Path == nil ||
291+
oldCrd.Spec.Conversion.Webhook.ClientConfig.Service.Port == nil ||
292+
!bytes.Equal(oldCrd.Spec.Conversion.Webhook.ClientConfig.CABundle, cert) {
293+
crd.Spec.Conversion = &apiextensionsv1.CustomResourceConversion{
294+
Strategy: apiextensionsv1.WebhookConverter,
295+
Webhook: &apiextensionsv1.WebhookConversion{
296+
ClientConfig: &apiextensionsv1.WebhookClientConfig{
297+
CABundle: []byte(cert),
298+
Service: &apiextensionsv1.ServiceReference{
299+
Namespace: ns,
300+
Name: func() string {
301+
if oldCrd.Spec.Conversion != nil && oldCrd.Spec.Conversion.Webhook != nil && oldCrd.Spec.Conversion.Webhook.ClientConfig != nil {
302+
return oldCrd.Spec.Conversion.Webhook.ClientConfig.Service.Name
303+
} else {
304+
return "radondb-mysql-webhook"
305+
}
306+
}(),
307+
Path: func() *string {
308+
var p string = "/convert"
309+
return &p
310+
}(),
311+
Port: func() *int32 {
312+
var serverPort int32 = 443
313+
return &serverPort
314+
}(),
315+
},
316+
},
317+
ConversionReviewVersions: []string{"v1"},
318+
},
319+
}
320+
log.Info("covert crd", "value", crd.Spec.Conversion)
321+
} else {
322+
return nil
323+
}
324+
errCRD = cli.Patch(ctx, crd, client.MergeFrom(oldCrd))
325+
if errCRD != nil {
326+
return errCRD
327+
}
328+
329+
return nil
330+
}
331+
332+
func RunUpdeteCRD(cli client.Client, log *logr.Logger) {
333+
go func() {
334+
// 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
335+
// if this process failed, just need to restart the operetor pod
336+
for i := 0; i < 100; i++ {
337+
time.Sleep(time.Second * 5)
338+
err := UpdateforCRD("mysqlclusters.mysql.radondb.com", cli, log)
339+
if err != nil {
340+
log.Info("update CRD failed", "error", err)
341+
}
342+
err = UpdateforCRD("backups.mysql.radondb.com", cli, log)
343+
if err != nil {
344+
log.Info("update CRD failed", "error", err)
345+
}
346+
}
347+
log.Info("check the crd about 8 minutes, now exit.")
348+
}()
349+
}

0 commit comments

Comments
 (0)