2020-09-16 14:19:51 +00:00
|
|
|
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
2020-08-07 21:49:04 +00:00
|
|
|
|
2020-08-09 17:04:05 +00:00
|
|
|
package controllermanager
|
2020-08-07 21:49:04 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
k8sinformers "k8s.io/client-go/informers"
|
|
|
|
"k8s.io/client-go/kubernetes"
|
|
|
|
restclient "k8s.io/client-go/rest"
|
2020-09-14 15:47:16 +00:00
|
|
|
"k8s.io/klog/v2/klogr"
|
2020-08-09 17:04:05 +00:00
|
|
|
aggregatorclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
|
2020-08-07 21:49:04 +00:00
|
|
|
|
2020-09-16 19:59:23 +00:00
|
|
|
pinnipedv1alpha1 "github.com/suzerain-io/pinniped/generated/1.19/apis/pinniped/v1alpha1"
|
2020-08-24 19:30:45 +00:00
|
|
|
pinnipedclientset "github.com/suzerain-io/pinniped/generated/1.19/client/clientset/versioned"
|
|
|
|
pinnipedinformers "github.com/suzerain-io/pinniped/generated/1.19/client/informers/externalversions"
|
2020-08-20 17:54:15 +00:00
|
|
|
"github.com/suzerain-io/pinniped/internal/controller/apicerts"
|
2020-09-14 15:47:16 +00:00
|
|
|
"github.com/suzerain-io/pinniped/internal/controller/identityprovider/idpcache"
|
|
|
|
"github.com/suzerain-io/pinniped/internal/controller/identityprovider/webhookcachecleaner"
|
|
|
|
"github.com/suzerain-io/pinniped/internal/controller/identityprovider/webhookcachefiller"
|
2020-08-21 16:55:44 +00:00
|
|
|
"github.com/suzerain-io/pinniped/internal/controller/issuerconfig"
|
2020-08-28 15:59:09 +00:00
|
|
|
"github.com/suzerain-io/pinniped/internal/controllerlib"
|
2020-08-20 17:54:15 +00:00
|
|
|
"github.com/suzerain-io/pinniped/internal/provider"
|
2020-08-07 21:49:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
singletonWorker = 1
|
|
|
|
defaultResyncInterval = 3 * time.Minute
|
|
|
|
)
|
|
|
|
|
|
|
|
// Prepare the controllers and their informers and return a function that will start them when called.
|
|
|
|
func PrepareControllers(
|
|
|
|
serverInstallationNamespace string,
|
|
|
|
discoveryURLOverride *string,
|
2020-08-11 01:53:53 +00:00
|
|
|
dynamicCertProvider provider.DynamicTLSServingCertProvider,
|
2020-08-20 19:17:18 +00:00
|
|
|
servingCertDuration time.Duration,
|
|
|
|
servingCertRenewBefore time.Duration,
|
2020-09-14 15:47:16 +00:00
|
|
|
idpCache *idpcache.Cache,
|
2020-08-07 21:49:04 +00:00
|
|
|
) (func(ctx context.Context), error) {
|
|
|
|
// Create k8s clients.
|
2020-08-20 17:54:15 +00:00
|
|
|
k8sClient, aggregatorClient, pinnipedClient, err := createClients()
|
2020-08-07 21:49:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not create clients for the controllers: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-08-09 17:04:05 +00:00
|
|
|
// Create informers. Don't forget to make sure they get started in the function returned below.
|
2020-08-20 17:54:15 +00:00
|
|
|
kubePublicNamespaceK8sInformers, installationNamespaceK8sInformers, installationNamespacePinnipedInformers :=
|
|
|
|
createInformers(serverInstallationNamespace, k8sClient, pinnipedClient)
|
2020-08-07 21:49:04 +00:00
|
|
|
|
2020-09-08 23:36:49 +00:00
|
|
|
// This string must match the name of the Service declared in the deployment yaml.
|
|
|
|
const serviceName = "pinniped-api"
|
|
|
|
|
2020-08-07 21:49:04 +00:00
|
|
|
// Create controller manager.
|
2020-08-28 15:59:09 +00:00
|
|
|
controllerManager := controllerlib.
|
2020-08-07 21:49:04 +00:00
|
|
|
NewManager().
|
|
|
|
WithController(
|
2020-08-21 16:55:44 +00:00
|
|
|
issuerconfig.NewPublisherController(
|
2020-08-07 21:49:04 +00:00
|
|
|
serverInstallationNamespace,
|
|
|
|
discoveryURLOverride,
|
2020-08-20 17:54:15 +00:00
|
|
|
pinnipedClient,
|
2020-08-09 17:04:05 +00:00
|
|
|
kubePublicNamespaceK8sInformers.Core().V1().ConfigMaps(),
|
2020-08-21 16:55:44 +00:00
|
|
|
installationNamespacePinnipedInformers.Crd().V1alpha1().CredentialIssuerConfigs(),
|
2020-08-28 15:59:09 +00:00
|
|
|
controllerlib.WithInformer,
|
2020-08-09 17:04:05 +00:00
|
|
|
),
|
|
|
|
singletonWorker,
|
|
|
|
).
|
|
|
|
WithController(
|
|
|
|
apicerts.NewCertsManagerController(
|
|
|
|
serverInstallationNamespace,
|
|
|
|
k8sClient,
|
|
|
|
installationNamespaceK8sInformers.Core().V1().Secrets(),
|
2020-08-28 15:59:09 +00:00
|
|
|
controllerlib.WithInformer,
|
|
|
|
controllerlib.WithInitialEvent,
|
2020-08-20 19:17:18 +00:00
|
|
|
servingCertDuration,
|
2020-09-08 23:36:49 +00:00
|
|
|
"Pinniped CA",
|
|
|
|
serviceName,
|
|
|
|
),
|
|
|
|
singletonWorker,
|
|
|
|
).
|
|
|
|
WithController(
|
|
|
|
apicerts.NewAPIServiceUpdaterController(
|
|
|
|
serverInstallationNamespace,
|
2020-09-16 19:59:23 +00:00
|
|
|
pinnipedv1alpha1.SchemeGroupVersion.Version+"."+pinnipedv1alpha1.GroupName,
|
2020-09-08 23:36:49 +00:00
|
|
|
aggregatorClient,
|
|
|
|
installationNamespaceK8sInformers.Core().V1().Secrets(),
|
|
|
|
controllerlib.WithInformer,
|
2020-08-09 17:04:05 +00:00
|
|
|
),
|
|
|
|
singletonWorker,
|
|
|
|
).
|
|
|
|
WithController(
|
|
|
|
apicerts.NewCertsObserverController(
|
|
|
|
serverInstallationNamespace,
|
|
|
|
dynamicCertProvider,
|
|
|
|
installationNamespaceK8sInformers.Core().V1().Secrets(),
|
2020-08-28 15:59:09 +00:00
|
|
|
controllerlib.WithInformer,
|
2020-08-07 21:49:04 +00:00
|
|
|
),
|
|
|
|
singletonWorker,
|
2020-08-19 20:15:45 +00:00
|
|
|
).
|
|
|
|
WithController(
|
|
|
|
apicerts.NewCertsExpirerController(
|
|
|
|
serverInstallationNamespace,
|
|
|
|
k8sClient,
|
|
|
|
installationNamespaceK8sInformers.Core().V1().Secrets(),
|
2020-08-28 15:59:09 +00:00
|
|
|
controllerlib.WithInformer,
|
2020-08-20 19:17:18 +00:00
|
|
|
servingCertRenewBefore,
|
2020-08-19 20:15:45 +00:00
|
|
|
),
|
|
|
|
singletonWorker,
|
2020-09-14 15:47:16 +00:00
|
|
|
).
|
|
|
|
WithController(
|
|
|
|
webhookcachefiller.New(
|
|
|
|
idpCache,
|
|
|
|
installationNamespacePinnipedInformers.IDP().V1alpha1().WebhookIdentityProviders(),
|
|
|
|
klogr.New(),
|
|
|
|
),
|
|
|
|
singletonWorker,
|
|
|
|
).
|
|
|
|
WithController(
|
|
|
|
webhookcachecleaner.New(
|
|
|
|
idpCache,
|
|
|
|
installationNamespacePinnipedInformers.IDP().V1alpha1().WebhookIdentityProviders(),
|
|
|
|
klogr.New(),
|
|
|
|
),
|
|
|
|
singletonWorker,
|
2020-08-07 21:49:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Return a function which starts the informers and controllers.
|
|
|
|
return func(ctx context.Context) {
|
2020-08-09 17:04:05 +00:00
|
|
|
kubePublicNamespaceK8sInformers.Start(ctx.Done())
|
|
|
|
installationNamespaceK8sInformers.Start(ctx.Done())
|
2020-08-20 17:54:15 +00:00
|
|
|
installationNamespacePinnipedInformers.Start(ctx.Done())
|
2020-08-09 17:04:05 +00:00
|
|
|
|
2020-09-15 18:52:08 +00:00
|
|
|
kubePublicNamespaceK8sInformers.WaitForCacheSync(ctx.Done())
|
|
|
|
installationNamespaceK8sInformers.WaitForCacheSync(ctx.Done())
|
|
|
|
installationNamespacePinnipedInformers.WaitForCacheSync(ctx.Done())
|
|
|
|
|
2020-08-07 21:49:04 +00:00
|
|
|
go controllerManager.Start(ctx)
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the k8s clients that will be used by the controllers.
|
2020-08-09 17:04:05 +00:00
|
|
|
func createClients() (
|
|
|
|
k8sClient *kubernetes.Clientset,
|
|
|
|
aggregatorClient *aggregatorclient.Clientset,
|
2020-08-20 17:54:15 +00:00
|
|
|
pinnipedClient *pinnipedclientset.Clientset,
|
2020-08-09 17:04:05 +00:00
|
|
|
err error,
|
|
|
|
) {
|
2020-08-19 18:21:07 +00:00
|
|
|
// Load the Kubernetes client configuration.
|
2020-08-07 21:49:04 +00:00
|
|
|
kubeConfig, err := restclient.InClusterConfig()
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, fmt.Errorf("could not load in-cluster configuration: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// explicitly use protobuf when talking to built-in kube APIs
|
|
|
|
protoKubeConfig := createProtoKubeConfig(kubeConfig)
|
|
|
|
|
|
|
|
// Connect to the core Kubernetes API.
|
2020-08-09 17:04:05 +00:00
|
|
|
k8sClient, err = kubernetes.NewForConfig(protoKubeConfig)
|
2020-08-07 21:49:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, fmt.Errorf("could not initialize Kubernetes client: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Connect to the Kubernetes aggregation API.
|
2020-08-09 17:04:05 +00:00
|
|
|
aggregatorClient, err = aggregatorclient.NewForConfig(protoKubeConfig)
|
2020-08-07 21:49:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, fmt.Errorf("could not initialize Kubernetes client: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-08-20 17:54:15 +00:00
|
|
|
// Connect to the pinniped API.
|
2020-08-07 21:49:04 +00:00
|
|
|
// I think we can't use protobuf encoding here because we are using CRDs
|
|
|
|
// (for which protobuf encoding is not supported).
|
2020-08-20 17:54:15 +00:00
|
|
|
pinnipedClient, err = pinnipedclientset.NewForConfig(kubeConfig)
|
2020-08-07 21:49:04 +00:00
|
|
|
if err != nil {
|
2020-08-20 17:54:15 +00:00
|
|
|
return nil, nil, nil, fmt.Errorf("could not initialize pinniped client: %w", err)
|
2020-08-07 21:49:04 +00:00
|
|
|
}
|
|
|
|
|
2020-08-09 17:04:05 +00:00
|
|
|
//nolint: nakedret
|
|
|
|
return
|
2020-08-07 21:49:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create the informers that will be used by the controllers.
|
|
|
|
func createInformers(
|
|
|
|
serverInstallationNamespace string,
|
|
|
|
k8sClient *kubernetes.Clientset,
|
2020-08-20 17:54:15 +00:00
|
|
|
pinnipedClient *pinnipedclientset.Clientset,
|
2020-08-09 17:04:05 +00:00
|
|
|
) (
|
|
|
|
kubePublicNamespaceK8sInformers k8sinformers.SharedInformerFactory,
|
|
|
|
installationNamespaceK8sInformers k8sinformers.SharedInformerFactory,
|
2020-08-20 17:54:15 +00:00
|
|
|
installationNamespacePinnipedInformers pinnipedinformers.SharedInformerFactory,
|
2020-08-09 17:04:05 +00:00
|
|
|
) {
|
|
|
|
kubePublicNamespaceK8sInformers = k8sinformers.NewSharedInformerFactoryWithOptions(
|
|
|
|
k8sClient,
|
|
|
|
defaultResyncInterval,
|
2020-08-21 16:55:44 +00:00
|
|
|
k8sinformers.WithNamespace(issuerconfig.ClusterInfoNamespace),
|
2020-08-09 17:04:05 +00:00
|
|
|
)
|
|
|
|
installationNamespaceK8sInformers = k8sinformers.NewSharedInformerFactoryWithOptions(
|
2020-08-07 21:49:04 +00:00
|
|
|
k8sClient,
|
|
|
|
defaultResyncInterval,
|
2020-08-09 17:04:05 +00:00
|
|
|
k8sinformers.WithNamespace(serverInstallationNamespace),
|
2020-08-07 21:49:04 +00:00
|
|
|
)
|
2020-08-20 17:54:15 +00:00
|
|
|
installationNamespacePinnipedInformers = pinnipedinformers.NewSharedInformerFactoryWithOptions(
|
|
|
|
pinnipedClient,
|
2020-08-07 21:49:04 +00:00
|
|
|
defaultResyncInterval,
|
2020-08-20 17:54:15 +00:00
|
|
|
pinnipedinformers.WithNamespace(serverInstallationNamespace),
|
2020-08-07 21:49:04 +00:00
|
|
|
)
|
2020-08-09 17:04:05 +00:00
|
|
|
return
|
2020-08-07 21:49:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a copy of the input config with the ContentConfig set to use protobuf.
|
|
|
|
// Do not use this config to communicate with any CRD based APIs.
|
|
|
|
func createProtoKubeConfig(kubeConfig *restclient.Config) *restclient.Config {
|
|
|
|
protoKubeConfig := restclient.CopyConfig(kubeConfig)
|
|
|
|
const protoThenJSON = runtime.ContentTypeProtobuf + "," + runtime.ContentTypeJSON
|
|
|
|
protoKubeConfig.AcceptContentTypes = protoThenJSON
|
|
|
|
protoKubeConfig.ContentType = runtime.ContentTypeProtobuf
|
|
|
|
return protoKubeConfig
|
|
|
|
}
|