mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-01-05 13:07:14 +00:00
155 lines
5.5 KiB
Go
155 lines
5.5 KiB
Go
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Package githubupstreamwatcher implements a controller which watches GitHubIdentityProviders.
|
|
package githubupstreamwatcher
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/go-logr/logr"
|
|
corev1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/api/equality"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/labels"
|
|
corev1informers "k8s.io/client-go/informers/core/v1"
|
|
|
|
"go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1"
|
|
supervisorclientset "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned"
|
|
idpinformers "go.pinniped.dev/generated/latest/client/supervisor/informers/externalversions/idp/v1alpha1"
|
|
pinnipedcontroller "go.pinniped.dev/internal/controller"
|
|
"go.pinniped.dev/internal/controller/conditionsutil"
|
|
"go.pinniped.dev/internal/controllerlib"
|
|
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
|
|
"go.pinniped.dev/internal/upstreamgithub"
|
|
)
|
|
|
|
const (
|
|
// Setup for the name of our controller in logs.
|
|
controllerName = "github-upstream-observer"
|
|
|
|
// Constants related to the client credentials Secret.
|
|
gitHubClientSecretType corev1.SecretType = "secrets.pinniped.dev/github-client"
|
|
|
|
// // fixes lint to split from above group where const has explicit type
|
|
// clientIDDataKey = "clientID"
|
|
// clientSecretDataKey = "clientSecret"
|
|
//
|
|
// // Constants related to conditions.
|
|
// typeClientCredentialsValid = "ClientCredentialsValid" //nolint:gosec // this is not a credential.
|
|
)
|
|
|
|
// UpstreamGitHubIdentityProviderICache is a thread safe cache that holds a list of validated upstream GitHub IDP configurations.
|
|
type UpstreamGitHubIdentityProviderICache interface {
|
|
SetGitHubIdentityProviders([]upstreamprovider.UpstreamGithubIdentityProviderI)
|
|
}
|
|
|
|
type gitHubWatcherController struct {
|
|
cache UpstreamGitHubIdentityProviderICache
|
|
log logr.Logger
|
|
client supervisorclientset.Interface
|
|
gitHubIdentityProviderInformer idpinformers.GitHubIdentityProviderInformer
|
|
secretInformer corev1informers.SecretInformer
|
|
}
|
|
|
|
// New instantiates a new controllerlib.Controller which will populate the provided UpstreamGitHubIdentityProviderICache.
|
|
func New(
|
|
idpCache UpstreamGitHubIdentityProviderICache,
|
|
client supervisorclientset.Interface,
|
|
gitHubIdentityProviderInformer idpinformers.GitHubIdentityProviderInformer,
|
|
secretInformer corev1informers.SecretInformer,
|
|
log logr.Logger,
|
|
withInformer pinnipedcontroller.WithInformerOptionFunc,
|
|
) controllerlib.Controller {
|
|
c := gitHubWatcherController{
|
|
cache: idpCache,
|
|
client: client,
|
|
log: log.WithName(controllerName),
|
|
gitHubIdentityProviderInformer: gitHubIdentityProviderInformer,
|
|
secretInformer: secretInformer,
|
|
}
|
|
|
|
return controllerlib.New(
|
|
controllerlib.Config{Name: controllerName, Syncer: &c},
|
|
withInformer(
|
|
gitHubIdentityProviderInformer,
|
|
pinnipedcontroller.MatchAnythingFilter(pinnipedcontroller.SingletonQueue()),
|
|
controllerlib.InformerOption{},
|
|
),
|
|
withInformer(
|
|
secretInformer,
|
|
pinnipedcontroller.MatchAnySecretOfTypeFilter(gitHubClientSecretType, pinnipedcontroller.SingletonQueue()),
|
|
controllerlib.InformerOption{},
|
|
),
|
|
)
|
|
}
|
|
|
|
// Sync implements controllerlib.Syncer.
|
|
func (c *gitHubWatcherController) Sync(ctx controllerlib.Context) error {
|
|
actualUpstreams, err := c.gitHubIdentityProviderInformer.Lister().List(labels.Everything())
|
|
if err != nil {
|
|
return fmt.Errorf("failed to list GitHubIdentityProviders: %w", err)
|
|
}
|
|
|
|
requeue := false
|
|
validatedUpstreams := make([]upstreamprovider.UpstreamGithubIdentityProviderI, 0, len(actualUpstreams))
|
|
for _, upstream := range actualUpstreams {
|
|
valid := c.validateUpstream(ctx, upstream)
|
|
if valid == nil {
|
|
requeue = true
|
|
} else {
|
|
validatedUpstreams = append(validatedUpstreams, upstreamprovider.UpstreamGithubIdentityProviderI(valid))
|
|
}
|
|
}
|
|
c.cache.SetGitHubIdentityProviders(validatedUpstreams)
|
|
if requeue {
|
|
return controllerlib.ErrSyntheticRequeue
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *gitHubWatcherController) validateUpstream(ctx controllerlib.Context, upstream *v1alpha1.GitHubIdentityProvider) *upstreamgithub.ProviderConfig {
|
|
result := upstreamgithub.ProviderConfig{
|
|
Name: upstream.Name,
|
|
}
|
|
|
|
conditions := []*metav1.Condition{
|
|
// TODO: once we firm up the proposal doc & merge, then firm up the CRD & merge, we can
|
|
// fill out these validations.
|
|
// c.validateHost(),
|
|
// c.validateTLS(),
|
|
// c.validateAllowedOrganizations(),
|
|
// c.validateOrganizationLoginPolicy(),
|
|
// c.validateClient(),
|
|
}
|
|
|
|
c.updateStatus(ctx.Context, upstream, conditions)
|
|
|
|
return &result
|
|
}
|
|
|
|
func (c *gitHubWatcherController) updateStatus(ctx context.Context, upstream *v1alpha1.GitHubIdentityProvider, conditions []*metav1.Condition) {
|
|
log := c.log.WithValues("namespace", upstream.Namespace, "name", upstream.Name)
|
|
updated := upstream.DeepCopy()
|
|
|
|
hadErrorCondition := conditionsutil.MergeIDPConditions(conditions, upstream.Generation, &updated.Status.Conditions, log)
|
|
|
|
updated.Status.Phase = v1alpha1.GitHubPhaseReady
|
|
if hadErrorCondition {
|
|
updated.Status.Phase = v1alpha1.GitHubPhaseError
|
|
}
|
|
|
|
if equality.Semantic.DeepEqual(upstream, updated) {
|
|
return
|
|
}
|
|
|
|
_, err := c.client.
|
|
IDPV1alpha1().
|
|
GitHubIdentityProviders(upstream.Namespace).
|
|
UpdateStatus(ctx, updated, metav1.UpdateOptions{})
|
|
if err != nil {
|
|
log.Error(err, "failed to update status")
|
|
}
|
|
}
|