github.com/oam-dev/cluster-gateway@v1.9.0/pkg/addon/controllers/installer.go (about) 1 package controllers 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "strconv" 8 "time" 9 10 "github.com/openshift/library-go/pkg/crypto" 11 "github.com/pkg/errors" 12 appsv1 "k8s.io/api/apps/v1" 13 corev1 "k8s.io/api/core/v1" 14 rbacv1 "k8s.io/api/rbac/v1" 15 apierrors "k8s.io/apimachinery/pkg/api/errors" 16 "k8s.io/apimachinery/pkg/api/meta" 17 "k8s.io/apimachinery/pkg/api/resource" 18 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 "k8s.io/apimachinery/pkg/runtime/schema" 20 "k8s.io/apimachinery/pkg/types" 21 "k8s.io/apimachinery/pkg/util/intstr" 22 "k8s.io/client-go/kubernetes" 23 corev1lister "k8s.io/client-go/listers/core/v1" 24 apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" 25 "k8s.io/utils/pointer" 26 "open-cluster-management.io/addon-framework/pkg/certrotation" 27 addonv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" 28 ocmauthv1alpha1 "open-cluster-management.io/managed-serviceaccount/api/v1alpha1" 29 ctrl "sigs.k8s.io/controller-runtime" 30 "sigs.k8s.io/controller-runtime/pkg/cache" 31 "sigs.k8s.io/controller-runtime/pkg/client" 32 "sigs.k8s.io/controller-runtime/pkg/handler" 33 "sigs.k8s.io/controller-runtime/pkg/reconcile" 34 35 clusterv1alpha1 "github.com/oam-dev/cluster-gateway/pkg/apis/cluster/v1alpha1" 36 proxyv1alpha1 "github.com/oam-dev/cluster-gateway/pkg/apis/proxy/v1alpha1" 37 "github.com/oam-dev/cluster-gateway/pkg/common" 38 "github.com/oam-dev/cluster-gateway/pkg/event" 39 "github.com/oam-dev/cluster-gateway/pkg/util/cert" 40 ) 41 42 var ( 43 log = ctrl.Log.WithName("ClusterGatewayInstaller") 44 ) 45 var _ reconcile.Reconciler = &ClusterGatewayInstaller{} 46 47 func SetupClusterGatewayInstallerWithManager(mgr ctrl.Manager, caPair *crypto.CA, nativeClient kubernetes.Interface, secretLister corev1lister.SecretLister) error { 48 installer := &ClusterGatewayInstaller{ 49 nativeClient: nativeClient, 50 caPair: caPair, 51 secretLister: secretLister, 52 cache: mgr.GetCache(), 53 client: mgr.GetClient(), 54 mapper: mgr.GetRESTMapper(), 55 } 56 return ctrl.NewControllerManagedBy(mgr). 57 // Watches ClusterManagementAddOn singleton 58 For(&addonv1alpha1.ClusterManagementAddOn{}). 59 // Watches ClusterGatewayConfiguration singleton 60 Watches(&proxyv1alpha1.ClusterGatewayConfiguration{}, 61 &event.ClusterGatewayConfigurationHandler{Client: mgr.GetClient()}). 62 // Watches ManagedClusterAddon. 63 Watches(&addonv1alpha1.ManagedClusterAddOn{}, 64 handler.EnqueueRequestForOwner(mgr.GetScheme(), mgr.GetRESTMapper(), &addonv1alpha1.ClusterManagementAddOn{})). 65 // Cluster-Gateway mTLS certificate should be actively reconciled 66 Watches(&corev1.Secret{}, 67 handler.EnqueueRequestForOwner(mgr.GetScheme(), mgr.GetRESTMapper(), &addonv1alpha1.ClusterManagementAddOn{})). 68 // Secrets rotated by ManagedServiceAccount should be actively reconciled 69 Watches(&corev1.Secret{}, &event.SecretHandler{}). 70 // Cluster-gateway apiserver instances should be actively reconciled 71 Watches(&appsv1.Deployment{}, 72 handler.EnqueueRequestForOwner(mgr.GetScheme(), mgr.GetRESTMapper(), &addonv1alpha1.ClusterManagementAddOn{})). 73 // APIService should be actively reconciled 74 Watches(&apiregistrationv1.APIService{}, &event.APIServiceHandler{WatchingName: common.ClusterGatewayAPIServiceName}). 75 Complete(installer) 76 } 77 78 type ClusterGatewayInstaller struct { 79 nativeClient kubernetes.Interface 80 secretLister corev1lister.SecretLister 81 caPair *crypto.CA 82 client client.Client 83 cache cache.Cache 84 mapper meta.RESTMapper 85 } 86 87 const ( 88 SecretNameClusterGatewayTLSCert = "cluster-gateway-tls-cert" 89 ServiceNameClusterGateway = "cluster-gateway" 90 ) 91 92 func (c *ClusterGatewayInstaller) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) { 93 // get the cluster-management-addon instance 94 log.Info("Start reconciling") 95 addon := &addonv1alpha1.ClusterManagementAddOn{} 96 if err := c.client.Get(ctx, request.NamespacedName, addon); err != nil { 97 if apierrors.IsNotFound(err) { 98 return reconcile.Result{}, nil 99 } 100 return reconcile.Result{}, errors.Wrapf(err, "failed to get cluster-management-addon: %v", request.Name) 101 } 102 if addon.Name != common.AddonName { 103 // skip 104 return reconcile.Result{}, nil 105 } 106 107 if addon.Spec.AddOnConfiguration.CRDName != common.ClusterGatewayConfigurationCRDName { 108 // skip 109 return reconcile.Result{}, nil 110 } 111 112 clusterGatewayConfiguration := &proxyv1alpha1.ClusterGatewayConfiguration{} 113 if err := c.client.Get(ctx, types.NamespacedName{ 114 Name: addon.Spec.AddOnConfiguration.CRName, 115 }, clusterGatewayConfiguration); err != nil { 116 if apierrors.IsNotFound(err) { 117 return reconcile.Result{}, fmt.Errorf("no such configuration: %v", addon.Spec.AddOnConfiguration.CRName) 118 } 119 return reconcile.Result{}, fmt.Errorf("failed getting configuration: %v", addon.Spec.AddOnConfiguration.CRName) 120 } 121 122 if err := c.ensureNamespace(clusterGatewayConfiguration.Spec.InstallNamespace); err != nil { 123 return reconcile.Result{}, errors.Wrapf(err, "failed to ensure required namespace") 124 } 125 if err := c.ensureNamespace(clusterGatewayConfiguration.Spec.SecretNamespace); err != nil { 126 return reconcile.Result{}, errors.Wrapf(err, "failed to ensure required namespace") 127 } 128 if err := c.ensureClusterProxySecrets(clusterGatewayConfiguration); err != nil { 129 return reconcile.Result{}, errors.Wrapf(err, "failed to ensure required proxy client related credentials") 130 } 131 if err := c.ensureSecretManagement(addon, clusterGatewayConfiguration); err != nil { 132 return reconcile.Result{}, errors.Wrapf(err, "failed to configure secret management") 133 } 134 135 sans := []string{ 136 ServiceNameClusterGateway, 137 ServiceNameClusterGateway + "." + clusterGatewayConfiguration.Spec.InstallNamespace, 138 ServiceNameClusterGateway + "." + clusterGatewayConfiguration.Spec.InstallNamespace + ".svc", 139 } 140 rotation := certrotation.TargetRotation{ 141 Namespace: clusterGatewayConfiguration.Spec.InstallNamespace, 142 Name: SecretNameClusterGatewayTLSCert, 143 HostNames: sans, 144 Validity: time.Hour * 24 * 180, 145 Lister: c.secretLister, 146 Client: c.nativeClient.CoreV1(), 147 } 148 if err := rotation.EnsureTargetCertKeyPair(c.caPair, c.caPair.Config.Certs); err != nil { 149 return reconcile.Result{}, errors.Wrapf(err, "failed rotating server tls cert") 150 } 151 152 caCertData, _, err := c.caPair.Config.GetPEMBytes() 153 if err != nil { 154 return reconcile.Result{}, errors.Wrapf(err, "failed encoding CA cert") 155 } 156 157 // create if not exists 158 namespace := clusterGatewayConfiguration.Spec.InstallNamespace 159 targets := []client.Object{ 160 newServiceAccount(addon, namespace), 161 newClusterGatewayService(addon, namespace), 162 newAuthenticationRole(addon, namespace), 163 newSecretRole(addon, clusterGatewayConfiguration.Spec.SecretNamespace), 164 newSecretRoleBinding(addon, namespace, clusterGatewayConfiguration.Spec.SecretNamespace), 165 newAPFClusterRole(addon), 166 newAPFClusterRoleBinding(addon, namespace), 167 newAPIService(addon, namespace, caCertData), 168 } 169 for _, obj := range targets { 170 if err := c.client.Create(context.TODO(), obj); err != nil { 171 if !apierrors.IsAlreadyExists(err) { 172 return reconcile.Result{}, errors.Wrapf(err, "failed deploying cluster-gateway") 173 } 174 } 175 } 176 177 if err := c.ensureClusterGatewayDeployment(addon, clusterGatewayConfiguration); err != nil { 178 return reconcile.Result{}, errors.Wrapf(err, "failed ensuring cluster-gateway deployment") 179 } 180 181 // always update apiservice 182 if err := c.ensureAPIService(addon, namespace); err != nil { 183 return reconcile.Result{}, errors.Wrapf(err, "failed ensuring cluster-gateway apiservice") 184 } 185 186 return reconcile.Result{}, nil 187 } 188 189 func (c *ClusterGatewayInstaller) ensureNamespace(namespace string) error { 190 ns := &corev1.Namespace{ 191 TypeMeta: metav1.TypeMeta{ 192 APIVersion: "v1", 193 Kind: "Namespace", 194 }, 195 ObjectMeta: metav1.ObjectMeta{ 196 Name: namespace, 197 }, 198 } 199 if _, err := c.nativeClient.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{}); err != nil { 200 if !apierrors.IsAlreadyExists(err) { 201 return err 202 } 203 } 204 return nil 205 } 206 207 func (c *ClusterGatewayInstaller) ensureAPIService(addon *addonv1alpha1.ClusterManagementAddOn, namespace string) error { 208 caCertData, _, err := c.caPair.Config.GetPEMBytes() 209 if err != nil { 210 return err 211 } 212 expected := newAPIService(addon, namespace, caCertData) 213 current := &apiregistrationv1.APIService{} 214 if err := c.client.Get(context.TODO(), types.NamespacedName{ 215 Name: expected.Name, 216 }, current); err != nil { 217 return err 218 } 219 if !bytes.Equal(caCertData, current.Spec.CABundle) { 220 expected.ResourceVersion = current.ResourceVersion 221 if err := c.client.Update(context.TODO(), expected); err != nil { 222 return err 223 } 224 } 225 return nil 226 } 227 228 func (c *ClusterGatewayInstaller) ensureClusterGatewayDeployment(addon *addonv1alpha1.ClusterManagementAddOn, config *proxyv1alpha1.ClusterGatewayConfiguration) error { 229 currentClusterGateway := &appsv1.Deployment{} 230 if err := c.client.Get(context.TODO(), types.NamespacedName{ 231 Namespace: config.Spec.InstallNamespace, 232 Name: "gateway-deployment", 233 }, currentClusterGateway); err != nil { 234 if apierrors.IsNotFound(err) { 235 clusterGateway := newClusterGatewayDeployment(addon, config) 236 if err := c.client.Create(context.TODO(), clusterGateway); err != nil { 237 return err 238 } 239 return nil 240 } 241 return err 242 } 243 genStr, ok := currentClusterGateway.Labels[labelKeyClusterGatewayConfigurationGeneration] 244 if ok { 245 gen, err := strconv.Atoi(genStr) 246 if err != nil { 247 return err 248 } 249 if config.Generation == int64(gen) { 250 return nil 251 } 252 } 253 254 clusterGateway := newClusterGatewayDeployment(addon, config) 255 clusterGateway.ResourceVersion = currentClusterGateway.ResourceVersion 256 if err := c.client.Update(context.TODO(), clusterGateway); err != nil { 257 return err 258 } 259 return nil 260 } 261 262 func (c *ClusterGatewayInstaller) ensureClusterProxySecrets(config *proxyv1alpha1.ClusterGatewayConfiguration) error { 263 if config.Spec.Egress.Type != proxyv1alpha1.EgressTypeClusterProxy { 264 return nil 265 } 266 proxyClientCASecretName := config.Spec.Egress.ClusterProxy.Credentials.ProxyClientCASecretName 267 err := cert.CopySecret(c.nativeClient, 268 config.Spec.Egress.ClusterProxy.Credentials.Namespace, proxyClientCASecretName, 269 config.Spec.InstallNamespace, proxyClientCASecretName) 270 if err != nil { 271 return errors.Wrapf(err, "failed copy secret %v", proxyClientCASecretName) 272 } 273 proxyClientSecretName := config.Spec.Egress.ClusterProxy.Credentials.ProxyClientSecretName 274 err = cert.CopySecret(c.nativeClient, 275 config.Spec.Egress.ClusterProxy.Credentials.Namespace, proxyClientSecretName, 276 config.Spec.InstallNamespace, proxyClientSecretName) 277 if err != nil { 278 return errors.Wrapf(err, "failed copy secret %v", proxyClientSecretName) 279 } 280 return nil 281 } 282 283 func (c *ClusterGatewayInstaller) ensureSecretManagement(clusterAddon *addonv1alpha1.ClusterManagementAddOn, config *proxyv1alpha1.ClusterGatewayConfiguration) error { 284 if config.Spec.SecretManagement.Type != proxyv1alpha1.SecretManagementTypeManagedServiceAccount { 285 return nil 286 } 287 if _, err := c.mapper.KindFor(schema.GroupVersionResource{ 288 Group: ocmauthv1alpha1.GroupVersion.Group, 289 Version: ocmauthv1alpha1.GroupVersion.Version, 290 Resource: "managedserviceaccounts", 291 }); err != nil { 292 return fmt.Errorf("failed to discover ManagedServiceAccount resource in the cluster") 293 } 294 addonList := &addonv1alpha1.ManagedClusterAddOnList{} 295 if err := c.client.List(context.TODO(), addonList); err != nil { 296 return errors.Wrapf(err, "failed to list managed cluster addons") 297 } 298 clusterGatewayAddon := make([]*addonv1alpha1.ManagedClusterAddOn, 0) 299 for _, addon := range addonList.Items { 300 addon := addon 301 if addon.Name == common.AddonName { 302 clusterGatewayAddon = append(clusterGatewayAddon, &addon) 303 } 304 } 305 for _, addon := range clusterGatewayAddon { 306 managedServiceAccount := buildManagedServiceAccount(addon) 307 if err := c.client.Create(context.TODO(), managedServiceAccount); err != nil { 308 if !apierrors.IsAlreadyExists(err) { 309 return errors.Wrapf(err, "failed to create managed serviceaccount") 310 } 311 } 312 313 if err := c.copySecretForManagedServiceAccount( 314 clusterAddon, 315 config, 316 addon.Namespace); err != nil { 317 return errors.Wrapf(err, "failed to copy secret from managed serviceaccount") 318 } 319 } 320 return nil 321 } 322 323 func (c *ClusterGatewayInstaller) copySecretForManagedServiceAccount(addon *addonv1alpha1.ClusterManagementAddOn, config *proxyv1alpha1.ClusterGatewayConfiguration, clusterName string) error { 324 endpointType := clusterv1alpha1.ClusterEndpointTypeConst 325 if config.Spec.Egress.Type == proxyv1alpha1.EgressTypeClusterProxy { 326 endpointType = clusterv1alpha1.ClusterEndpointTypeClusterProxy 327 } 328 gatewaySecretNamespace := config.Spec.SecretNamespace 329 secretName := config.Spec.SecretManagement.ManagedServiceAccount.Name 330 331 secret, err := c.secretLister.Secrets(clusterName). 332 Get(secretName) 333 if err != nil { 334 if !apierrors.IsNotFound(err) { 335 return errors.Wrapf(err, "failed to get token secret") 336 } 337 return nil 338 } 339 currentSecret, err := c.secretLister.Secrets(gatewaySecretNamespace).Get(clusterName) 340 shouldCreate := false 341 if err != nil { 342 if !apierrors.IsNotFound(err) { 343 return errors.Wrapf(err, "failed to get the cluster secret") 344 } 345 shouldCreate = true 346 } 347 if shouldCreate { 348 if _, err := c.nativeClient.CoreV1().Secrets(gatewaySecretNamespace). 349 Create(context.TODO(), 350 &corev1.Secret{ 351 ObjectMeta: metav1.ObjectMeta{ 352 Namespace: gatewaySecretNamespace, 353 Name: clusterName, 354 Labels: map[string]string{ 355 common.LabelKeyClusterCredentialType: string(clusterv1alpha1.CredentialTypeServiceAccountToken), 356 common.LabelKeyClusterEndpointType: string(endpointType), 357 }, 358 OwnerReferences: []metav1.OwnerReference{ 359 { 360 APIVersion: addonv1alpha1.GroupVersion.String(), 361 Kind: "ClusterManagementAddOn", 362 UID: addon.UID, 363 Name: addon.Name, 364 }, 365 }, 366 }, 367 Type: corev1.SecretTypeOpaque, 368 Data: map[string][]byte{ 369 corev1.ServiceAccountRootCAKey: secret.Data[corev1.ServiceAccountRootCAKey], 370 corev1.ServiceAccountTokenKey: secret.Data[corev1.ServiceAccountTokenKey], 371 }, 372 }, 373 metav1.CreateOptions{}); err != nil { 374 return errors.Wrapf(err, "failed to create the cluster secret") 375 } 376 } else { 377 if bytes.Equal(secret.Data[corev1.ServiceAccountTokenKey], currentSecret.Data[corev1.ServiceAccountTokenKey]) { 378 return nil // no need for an update 379 } 380 currentSecret.Data[corev1.ServiceAccountRootCAKey] = secret.Data[corev1.ServiceAccountRootCAKey] 381 currentSecret.Data[corev1.ServiceAccountTokenKey] = secret.Data[corev1.ServiceAccountTokenKey] 382 if _, err := c.nativeClient.CoreV1().Secrets(gatewaySecretNamespace). 383 Update(context.TODO(), currentSecret, metav1.UpdateOptions{}); err != nil { 384 return errors.Wrapf(err, "failed to update the cluster secret") 385 } 386 } 387 return nil 388 } 389 390 func newServiceAccount(addon *addonv1alpha1.ClusterManagementAddOn, namespace string) *corev1.ServiceAccount { 391 return &corev1.ServiceAccount{ 392 TypeMeta: metav1.TypeMeta{ 393 APIVersion: "v1", 394 Kind: "ServiceAccount", 395 }, 396 ObjectMeta: metav1.ObjectMeta{ 397 Namespace: namespace, 398 Name: common.AddonName, 399 OwnerReferences: []metav1.OwnerReference{ 400 { 401 APIVersion: addonv1alpha1.GroupVersion.String(), 402 Kind: "ClusterManagementAddOn", 403 UID: addon.UID, 404 Name: addon.Name, 405 }, 406 }, 407 }, 408 } 409 } 410 411 const labelKeyClusterGatewayConfigurationGeneration = "proxy.open-cluster-management.io/configuration-generation" 412 413 func newClusterGatewayDeployment(addon *addonv1alpha1.ClusterManagementAddOn, config *proxyv1alpha1.ClusterGatewayConfiguration) *appsv1.Deployment { 414 args := []string{ 415 "--secure-port=9443", 416 "--secret-namespace=" + config.Spec.SecretNamespace, 417 "--ocm-integration=true", 418 "--tls-cert-file=/etc/server/tls.crt", 419 "--tls-private-key-file=/etc/server/tls.key", 420 "--feature-gates=HealthinessCheck=true,SecretCache=true", 421 } 422 volumes := []corev1.Volume{ 423 { 424 Name: "server", 425 VolumeSource: corev1.VolumeSource{ 426 Secret: &corev1.SecretVolumeSource{ 427 SecretName: SecretNameClusterGatewayTLSCert, 428 }, 429 }, 430 }, 431 } 432 volumeMounts := []corev1.VolumeMount{ 433 { 434 Name: "server", 435 MountPath: "/etc/server/", 436 ReadOnly: true, 437 }, 438 } 439 if config.Spec.Egress.Type == proxyv1alpha1.EgressTypeClusterProxy { 440 args = append(args, 441 "--proxy-host="+config.Spec.Egress.ClusterProxy.ProxyServerHost, 442 "--proxy-port="+strconv.Itoa(int(config.Spec.Egress.ClusterProxy.ProxyServerPort)), 443 "--proxy-ca-cert=/etc/ca/ca.crt", 444 "--proxy-cert=/etc/tls/tls.crt", 445 "--proxy-key=/etc/tls/tls.key", 446 ) 447 volumes = append(volumes, 448 corev1.Volume{ 449 Name: "proxy-client-ca", 450 VolumeSource: corev1.VolumeSource{ 451 Secret: &corev1.SecretVolumeSource{ 452 SecretName: "proxy-server-ca", 453 }, 454 }, 455 }, 456 corev1.Volume{ 457 Name: "proxy-client", 458 VolumeSource: corev1.VolumeSource{ 459 Secret: &corev1.SecretVolumeSource{ 460 SecretName: "proxy-client", 461 }, 462 }, 463 }, 464 ) 465 volumeMounts = append(volumeMounts, 466 corev1.VolumeMount{ 467 Name: "proxy-client-ca", 468 MountPath: "/etc/ca/", 469 }, 470 corev1.VolumeMount{ 471 Name: "proxy-client", 472 MountPath: "/etc/tls/", 473 }, 474 ) 475 } 476 477 maxUnavailable := intstr.FromInt(1) 478 maxSurge := intstr.FromInt(1) 479 deploy := &appsv1.Deployment{ 480 TypeMeta: metav1.TypeMeta{ 481 APIVersion: "apps/v1", 482 Kind: "Deployment", 483 }, 484 ObjectMeta: metav1.ObjectMeta{ 485 Namespace: config.Spec.InstallNamespace, 486 Name: "gateway-deployment", 487 OwnerReferences: []metav1.OwnerReference{ 488 { 489 APIVersion: addonv1alpha1.GroupVersion.String(), 490 Kind: "ClusterManagementAddOn", 491 UID: addon.UID, 492 Name: addon.Name, 493 }, 494 }, 495 Labels: map[string]string{ 496 labelKeyClusterGatewayConfigurationGeneration: strconv.Itoa(int(config.Generation)), 497 }, 498 }, 499 Spec: appsv1.DeploymentSpec{ 500 Selector: &metav1.LabelSelector{ 501 MatchLabels: map[string]string{ 502 common.LabelKeyOpenClusterManagementAddon: common.AddonName, 503 }, 504 }, 505 Replicas: pointer.Int32(3), 506 Template: corev1.PodTemplateSpec{ 507 ObjectMeta: metav1.ObjectMeta{ 508 Labels: map[string]string{ 509 common.LabelKeyOpenClusterManagementAddon: common.AddonName, 510 }, 511 }, 512 Spec: corev1.PodSpec{ 513 Containers: []corev1.Container{ 514 { 515 Name: "apiserver", 516 Image: config.Spec.Image, 517 ImagePullPolicy: corev1.PullIfNotPresent, 518 Args: args, 519 VolumeMounts: volumeMounts, 520 Resources: corev1.ResourceRequirements{ 521 Requests: corev1.ResourceList{ 522 corev1.ResourceCPU: *resource.NewMilliQuantity(100, resource.DecimalSI), 523 corev1.ResourceMemory: *resource.NewQuantity(200*1024*1024, resource.BinarySI), 524 }, 525 Limits: corev1.ResourceList{ 526 corev1.ResourceCPU: *resource.NewMilliQuantity(500, resource.DecimalSI), 527 corev1.ResourceMemory: *resource.NewQuantity(600*1024*1024, resource.BinarySI), 528 }, 529 }, 530 }, 531 }, 532 ServiceAccountName: common.AddonName, 533 Volumes: volumes, 534 }, 535 }, 536 Strategy: appsv1.DeploymentStrategy{ 537 Type: appsv1.RollingUpdateDeploymentStrategyType, 538 RollingUpdate: &appsv1.RollingUpdateDeployment{ 539 540 MaxUnavailable: &maxUnavailable, 541 MaxSurge: &maxSurge, 542 }, 543 }, 544 }, 545 } 546 return deploy 547 } 548 549 func newClusterGatewayService(addon *addonv1alpha1.ClusterManagementAddOn, namespace string) *corev1.Service { 550 return &corev1.Service{ 551 TypeMeta: metav1.TypeMeta{ 552 APIVersion: "v1", 553 Kind: "Service", 554 }, 555 ObjectMeta: metav1.ObjectMeta{ 556 Namespace: namespace, 557 Name: common.AddonName, 558 OwnerReferences: []metav1.OwnerReference{ 559 { 560 APIVersion: addonv1alpha1.GroupVersion.String(), 561 Kind: "ClusterManagementAddOn", 562 UID: addon.UID, 563 Name: addon.Name, 564 }, 565 }, 566 }, 567 Spec: corev1.ServiceSpec{ 568 Type: corev1.ServiceTypeClusterIP, 569 Selector: map[string]string{ 570 common.LabelKeyOpenClusterManagementAddon: common.AddonName, 571 }, 572 Ports: []corev1.ServicePort{ 573 { 574 Name: "https", 575 Port: 9443, 576 }, 577 }, 578 }, 579 } 580 } 581 582 func newAPIService(addon *addonv1alpha1.ClusterManagementAddOn, namespace string, verifyingCABundle []byte) *apiregistrationv1.APIService { 583 return &apiregistrationv1.APIService{ 584 ObjectMeta: metav1.ObjectMeta{ 585 Name: "v1alpha1.cluster.core.oam.dev", 586 OwnerReferences: []metav1.OwnerReference{ 587 { 588 APIVersion: addonv1alpha1.GroupVersion.String(), 589 Kind: "ClusterManagementAddOn", 590 UID: addon.UID, 591 Name: addon.Name, 592 }, 593 }, 594 }, 595 Spec: apiregistrationv1.APIServiceSpec{ 596 Group: "cluster.core.oam.dev", 597 Version: "v1alpha1", 598 Service: &apiregistrationv1.ServiceReference{ 599 Namespace: namespace, 600 Name: common.AddonName, 601 Port: pointer.Int32(9443), 602 }, 603 GroupPriorityMinimum: 5000, 604 VersionPriority: 10, 605 CABundle: verifyingCABundle, 606 }, 607 } 608 } 609 610 func newAuthenticationRole(addon *addonv1alpha1.ClusterManagementAddOn, namespace string) *rbacv1.RoleBinding { 611 return &rbacv1.RoleBinding{ 612 ObjectMeta: metav1.ObjectMeta{ 613 Namespace: "kube-system", 614 Name: "extension-apiserver-authentication-reader:cluster-gateway", 615 OwnerReferences: []metav1.OwnerReference{ 616 { 617 APIVersion: addonv1alpha1.GroupVersion.String(), 618 Kind: "ClusterManagementAddOn", 619 UID: addon.UID, 620 Name: addon.Name, 621 }, 622 }, 623 }, 624 RoleRef: rbacv1.RoleRef{ 625 Kind: "Role", 626 Name: "extension-apiserver-authentication-reader", 627 }, 628 Subjects: []rbacv1.Subject{ 629 { 630 Kind: rbacv1.ServiceAccountKind, 631 Namespace: namespace, 632 Name: common.AddonName, 633 }, 634 }, 635 } 636 } 637 638 func newSecretRole(addon *addonv1alpha1.ClusterManagementAddOn, secretNamespace string) *rbacv1.Role { 639 return &rbacv1.Role{ 640 ObjectMeta: metav1.ObjectMeta{ 641 Namespace: secretNamespace, 642 Name: "cluster-gateway", 643 OwnerReferences: []metav1.OwnerReference{ 644 { 645 APIVersion: addonv1alpha1.GroupVersion.String(), 646 Kind: "ClusterManagementAddOn", 647 UID: addon.UID, 648 Name: addon.Name, 649 }, 650 }, 651 }, 652 Rules: []rbacv1.PolicyRule{ 653 { 654 APIGroups: []string{""}, 655 Resources: []string{"secrets"}, 656 Verbs: []string{"get", "list", "watch", "update"}, 657 }, 658 }, 659 } 660 } 661 662 func newSecretRoleBinding(addon *addonv1alpha1.ClusterManagementAddOn, namespace, secretNamespace string) *rbacv1.RoleBinding { 663 return &rbacv1.RoleBinding{ 664 ObjectMeta: metav1.ObjectMeta{ 665 Namespace: secretNamespace, 666 Name: "cluster-gateway", 667 OwnerReferences: []metav1.OwnerReference{ 668 { 669 APIVersion: addonv1alpha1.GroupVersion.String(), 670 Kind: "ClusterManagementAddOn", 671 UID: addon.UID, 672 Name: addon.Name, 673 }, 674 }, 675 }, 676 RoleRef: rbacv1.RoleRef{ 677 Kind: "Role", 678 Name: "cluster-gateway", 679 }, 680 Subjects: []rbacv1.Subject{ 681 { 682 Kind: rbacv1.ServiceAccountKind, 683 Namespace: namespace, 684 Name: common.AddonName, 685 }, 686 }, 687 } 688 } 689 func newAPFClusterRole(addon *addonv1alpha1.ClusterManagementAddOn) *rbacv1.ClusterRole { 690 return &rbacv1.ClusterRole{ 691 ObjectMeta: metav1.ObjectMeta{ 692 Name: "apiserver-aggregation:cluster-gateway", 693 OwnerReferences: []metav1.OwnerReference{ 694 { 695 APIVersion: addonv1alpha1.GroupVersion.String(), 696 Kind: "ClusterManagementAddOn", 697 UID: addon.UID, 698 Name: addon.Name, 699 }, 700 }, 701 }, 702 Rules: []rbacv1.PolicyRule{ 703 { 704 APIGroups: []string{"cluster.open-cluster-management.io"}, 705 Resources: []string{"managedclusters"}, 706 Verbs: []string{"get", "list", "watch"}, 707 }, 708 { 709 APIGroups: []string{""}, 710 Resources: []string{"namespaces"}, 711 Verbs: []string{"get", "list", "watch"}, 712 }, 713 { 714 APIGroups: []string{"admissionregistration.k8s.io"}, 715 Resources: []string{"mutatingwebhookconfigurations", "validatingwebhookconfigurations"}, 716 Verbs: []string{"get", "list", "watch"}, 717 }, 718 { 719 APIGroups: []string{"flowcontrol.apiserver.k8s.io"}, 720 Resources: []string{"prioritylevelconfigurations", "flowschemas"}, 721 Verbs: []string{"get", "list", "watch"}, 722 }, 723 { 724 APIGroups: []string{"authorization.k8s.io"}, 725 Resources: []string{"subjectaccessreviews"}, 726 Verbs: []string{"*"}, 727 }, 728 }, 729 } 730 } 731 732 func newAPFClusterRoleBinding(addon *addonv1alpha1.ClusterManagementAddOn, namespace string) *rbacv1.ClusterRoleBinding { 733 return &rbacv1.ClusterRoleBinding{ 734 ObjectMeta: metav1.ObjectMeta{ 735 Name: "apiserver-aggregation:cluster-gateway", 736 OwnerReferences: []metav1.OwnerReference{ 737 { 738 APIVersion: addonv1alpha1.GroupVersion.String(), 739 Kind: "ClusterManagementAddOn", 740 UID: addon.UID, 741 Name: addon.Name, 742 }, 743 }, 744 }, 745 RoleRef: rbacv1.RoleRef{ 746 Kind: "ClusterRole", 747 Name: "apiserver-aggregation:cluster-gateway", 748 }, 749 Subjects: []rbacv1.Subject{ 750 { 751 Kind: rbacv1.ServiceAccountKind, 752 Namespace: namespace, 753 Name: common.AddonName, 754 }, 755 }, 756 } 757 } 758 759 func buildManagedServiceAccount(addon *addonv1alpha1.ManagedClusterAddOn) *ocmauthv1alpha1.ManagedServiceAccount { 760 return &ocmauthv1alpha1.ManagedServiceAccount{ 761 TypeMeta: metav1.TypeMeta{ 762 APIVersion: "authentication.open-cluster-management.io/v1alpha1", 763 Kind: "ManagedServiceAccount", 764 }, 765 ObjectMeta: metav1.ObjectMeta{ 766 Namespace: addon.Namespace, 767 Name: common.AddonName, 768 OwnerReferences: []metav1.OwnerReference{ 769 { 770 APIVersion: addonv1alpha1.GroupVersion.String(), 771 Kind: "ManagedClusterAddOn", 772 UID: addon.UID, 773 Name: addon.Name, 774 }, 775 }, 776 }, 777 Spec: ocmauthv1alpha1.ManagedServiceAccountSpec{ 778 Rotation: ocmauthv1alpha1.ManagedServiceAccountRotation{ 779 Enabled: true, 780 Validity: metav1.Duration{ 781 Duration: time.Hour * 24 * 180, 782 }, 783 }, 784 }, 785 } 786 }