@@ -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
4247const (
@@ -207,3 +212,119 @@ 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 = "dmp-extension"
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+ if oldCrd .Spec .Conversion == nil || oldCrd .Spec .Conversion .Webhook == nil || oldCrd .Spec .Conversion .Webhook .ClientConfig == nil ||
288+ oldCrd .Spec .Conversion .Webhook .ClientConfig .CABundle == nil || ! bytes .Equal (oldCrd .Spec .Conversion .Webhook .ClientConfig .CABundle , cert ) {
289+ log .Info ("covert crd" , "value" , ! bytes .Equal (oldCrd .Spec .Conversion .Webhook .ClientConfig .CABundle , cert ))
290+ crd .Spec .Conversion = & apiextensionsv1.CustomResourceConversion {
291+ Strategy : apiextensionsv1 .WebhookConverter ,
292+ Webhook : & apiextensionsv1.WebhookConversion {
293+ ClientConfig : & apiextensionsv1.WebhookClientConfig {
294+ CABundle : []byte (cert ),
295+ Service : & apiextensionsv1.ServiceReference {
296+ Namespace : ns ,
297+ },
298+ },
299+ ConversionReviewVersions : []string {"v1" },
300+ },
301+ }
302+ } else {
303+ return nil
304+ }
305+ errCRD = cli .Patch (ctx , crd , client .MergeFrom (oldCrd ))
306+ if errCRD != nil {
307+ return errCRD
308+ }
309+
310+ return nil
311+ }
312+
313+ func RunUpdeteCRD (cli client.Client , log * logr.Logger ) {
314+ go func () {
315+ // 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
316+ // if this process failed, just need to restart the operetor pod
317+ for i := 0 ; i < 100 ; i ++ {
318+ time .Sleep (time .Second * 5 )
319+ err := UpdateforCRD ("mysqlclusters.mysql.radondb.com" , cli , log )
320+ if err != nil {
321+ log .Info ("update CRD failed" , "error" , err )
322+ }
323+ err = UpdateforCRD ("backups.mysql.radondb.com" , cli , log )
324+ if err != nil {
325+ log .Info ("update CRD failed" , "error" , err )
326+ }
327+ }
328+ log .Info ("check the crd about 8 minutes, now exit." )
329+ }()
330+ }
0 commit comments