86c3f89b2e
- Refactors the existing cert generation code into controllers which read and write a Secret containing the certs - Does not add any new functionality yet, e.g. no new handling for cert expiration, and no leader election to allow for multiple servers running simultaneously - This commit also doesn't add new tests for the cert generation code, but it should be more unit testable now as controllers
175 lines
5.2 KiB
Go
175 lines
5.2 KiB
Go
/*
|
|
Copyright 2020 VMware, Inc.
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package logindiscovery
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"fmt"
|
|
|
|
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
corev1informers "k8s.io/client-go/informers/core/v1"
|
|
"k8s.io/client-go/tools/clientcmd"
|
|
"k8s.io/klog/v2"
|
|
|
|
"github.com/suzerain-io/controller-go"
|
|
placeholdernamecontroller "github.com/suzerain-io/placeholder-name/internal/controller"
|
|
crdsplaceholderv1alpha1 "github.com/suzerain-io/placeholder-name/kubernetes/1.19/api/apis/crdsplaceholder/v1alpha1"
|
|
placeholderclientset "github.com/suzerain-io/placeholder-name/kubernetes/1.19/client-go/clientset/versioned"
|
|
crdsplaceholderv1alpha1informers "github.com/suzerain-io/placeholder-name/kubernetes/1.19/client-go/informers/externalversions/crdsplaceholder/v1alpha1"
|
|
)
|
|
|
|
const (
|
|
ClusterInfoNamespace = "kube-public"
|
|
|
|
clusterInfoName = "cluster-info"
|
|
clusterInfoConfigMapKey = "kubeconfig"
|
|
|
|
configName = "placeholder-name-config"
|
|
)
|
|
|
|
type publisherController struct {
|
|
namespace string
|
|
serverOverride *string
|
|
placeholderClient placeholderclientset.Interface
|
|
configMapInformer corev1informers.ConfigMapInformer
|
|
loginDiscoveryConfigInformer crdsplaceholderv1alpha1informers.LoginDiscoveryConfigInformer
|
|
}
|
|
|
|
func NewPublisherController(
|
|
namespace string,
|
|
serverOverride *string,
|
|
placeholderClient placeholderclientset.Interface,
|
|
configMapInformer corev1informers.ConfigMapInformer,
|
|
loginDiscoveryConfigInformer crdsplaceholderv1alpha1informers.LoginDiscoveryConfigInformer,
|
|
withInformer placeholdernamecontroller.WithInformerOptionFunc,
|
|
) controller.Controller {
|
|
return controller.New(
|
|
controller.Config{
|
|
Name: "publisher-controller",
|
|
Syncer: &publisherController{
|
|
namespace: namespace,
|
|
serverOverride: serverOverride,
|
|
placeholderClient: placeholderClient,
|
|
configMapInformer: configMapInformer,
|
|
loginDiscoveryConfigInformer: loginDiscoveryConfigInformer,
|
|
},
|
|
},
|
|
withInformer(
|
|
configMapInformer,
|
|
placeholdernamecontroller.NameAndNamespaceExactMatchFilterFactory(clusterInfoName, ClusterInfoNamespace),
|
|
controller.InformerOption{},
|
|
),
|
|
withInformer(
|
|
loginDiscoveryConfigInformer,
|
|
placeholdernamecontroller.NameAndNamespaceExactMatchFilterFactory(configName, namespace),
|
|
controller.InformerOption{},
|
|
),
|
|
)
|
|
}
|
|
|
|
func (c *publisherController) Sync(ctx controller.Context) error {
|
|
configMap, err := c.configMapInformer.
|
|
Lister().
|
|
ConfigMaps(ClusterInfoNamespace).
|
|
Get(clusterInfoName)
|
|
notFound := k8serrors.IsNotFound(err)
|
|
if err != nil && !notFound {
|
|
return fmt.Errorf("failed to get %s configmap: %w", clusterInfoName, err)
|
|
}
|
|
if notFound {
|
|
klog.InfoS(
|
|
"could not find config map",
|
|
"configmap",
|
|
klog.KRef(ClusterInfoNamespace, clusterInfoName),
|
|
)
|
|
return nil
|
|
}
|
|
|
|
kubeConfig, kubeConfigPresent := configMap.Data[clusterInfoConfigMapKey]
|
|
if !kubeConfigPresent {
|
|
klog.InfoS("could not find kubeconfig configmap key")
|
|
return nil
|
|
}
|
|
|
|
config, _ := clientcmd.Load([]byte(kubeConfig))
|
|
|
|
var certificateAuthorityData, server string
|
|
for _, v := range config.Clusters {
|
|
certificateAuthorityData = base64.StdEncoding.EncodeToString(v.CertificateAuthorityData)
|
|
server = v.Server
|
|
break
|
|
}
|
|
|
|
if c.serverOverride != nil {
|
|
server = *c.serverOverride
|
|
}
|
|
|
|
discoveryConfig := crdsplaceholderv1alpha1.LoginDiscoveryConfig{
|
|
TypeMeta: metav1.TypeMeta{},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: configName,
|
|
Namespace: c.namespace,
|
|
},
|
|
Spec: crdsplaceholderv1alpha1.LoginDiscoveryConfigSpec{
|
|
Server: server,
|
|
CertificateAuthorityData: certificateAuthorityData,
|
|
},
|
|
}
|
|
if err := c.createOrUpdateLoginDiscoveryConfig(ctx.Context, &discoveryConfig); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *publisherController) createOrUpdateLoginDiscoveryConfig(
|
|
ctx context.Context,
|
|
discoveryConfig *crdsplaceholderv1alpha1.LoginDiscoveryConfig,
|
|
) error {
|
|
existingDiscoveryConfig, err := c.loginDiscoveryConfigInformer.
|
|
Lister().
|
|
LoginDiscoveryConfigs(c.namespace).
|
|
Get(discoveryConfig.Name)
|
|
notFound := k8serrors.IsNotFound(err)
|
|
if err != nil && !notFound {
|
|
return fmt.Errorf("could not get logindiscoveryconfig: %w", err)
|
|
}
|
|
|
|
loginDiscoveryConfigs := c.placeholderClient.
|
|
CrdsV1alpha1().
|
|
LoginDiscoveryConfigs(c.namespace)
|
|
if notFound {
|
|
if _, err := loginDiscoveryConfigs.Create(
|
|
ctx,
|
|
discoveryConfig,
|
|
metav1.CreateOptions{},
|
|
); err != nil {
|
|
return fmt.Errorf("could not create logindiscoveryconfig: %w", err)
|
|
}
|
|
} else if !equal(existingDiscoveryConfig, discoveryConfig) {
|
|
// Update just the fields we care about.
|
|
existingDiscoveryConfig.Spec.Server = discoveryConfig.Spec.Server
|
|
existingDiscoveryConfig.Spec.CertificateAuthorityData = discoveryConfig.Spec.CertificateAuthorityData
|
|
|
|
if _, err := loginDiscoveryConfigs.Update(
|
|
ctx,
|
|
existingDiscoveryConfig,
|
|
metav1.UpdateOptions{},
|
|
); err != nil {
|
|
return fmt.Errorf("could not update logindiscoveryconfig: %w", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func equal(a, b *crdsplaceholderv1alpha1.LoginDiscoveryConfig) bool {
|
|
return a.Spec.Server == b.Spec.Server &&
|
|
a.Spec.CertificateAuthorityData == b.Spec.CertificateAuthorityData
|
|
}
|