ContainerImage.Pinniped/internal/autoregistration/autoregistration.go
Ryan Richard 08961919b5 Fix a garbage collection bug
- Previously the golang code would create a Service and an APIService.
  The APIService would be given an owner reference which pointed to
  the namespace in which the app was installed.
- This prevented the app from being uninstalled. The namespace would
  refuse to delete, so `kapp delete` or `kubectl delete` would fail.
- The new approach is to statically define the Service and an APIService
  in the deployment.yaml, except for the caBundle of the APIService.
  Then the golang code will perform an update to add the caBundle at
  runtime.
- When the user uses `kapp deploy` or `kubectl apply` either tool will
  notice that the caBundle is not declared in the yaml and will
  therefore avoid editing that field.
- When the user uses `kapp delete` or `kubectl delete` either tool will
  destroy the objects because they are statically declared with names
  in the yaml, just like all of the other objects. There are no
  ownerReferences used, so nothing should prevent the namespace from
  being deleted.
- This approach also allows us to have less golang code to maintain.
- In the future, if our golang controllers want to dynamically add
  an Ingress or other objects, they can still do that. An Ingress
  would point to our statically defined Service as its backend.

Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2020-08-04 16:46:27 -07:00

43 lines
1.5 KiB
Go

/*
Copyright 2020 VMware, Inc.
SPDX-License-Identifier: Apache-2.0
*/
// Package autoregistration updates the pre-registered APIService.
package autoregistration
import (
"context"
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/util/retry"
aggregatationv1client "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
placeholderv1alpha1 "github.com/suzerain-io/placeholder-name-api/pkg/apis/placeholder/v1alpha1"
)
// UpdateAPIService updates the APIService's CA bundle.
func UpdateAPIService(ctx context.Context, aggregationV1 aggregatationv1client.Interface, aggregatedAPIServerCA []byte) error {
apiServices := aggregationV1.ApiregistrationV1().APIServices()
apiServiceName := placeholderv1alpha1.SchemeGroupVersion.Version + "." + placeholderv1alpha1.GroupName
if err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
// Retrieve the latest version of the Service before attempting update.
// RetryOnConflict uses exponential backoff to avoid exhausting the API server.
fetchedAPIService, err := apiServices.Get(ctx, apiServiceName, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("could not get existing version of API service: %w", err)
}
// Update just the field we care about.
fetchedAPIService.Spec.CABundle = aggregatedAPIServerCA
_, updateErr := apiServices.Update(ctx, fetchedAPIService, metav1.UpdateOptions{})
return updateErr
}); err != nil {
return fmt.Errorf("could not update API service: %w", err)
}
return nil
}