Skip to content

Commit

Permalink
Merge pull request #6832 from ykakarap/runtime-sdk_logging
Browse files Browse the repository at this point in the history
🌱 runtime-sdk: add logs
  • Loading branch information
k8s-ci-robot authored Jul 11, 2022
2 parents b2605ab + 4843b49 commit 6df168f
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 2 deletions.
10 changes: 9 additions & 1 deletion exp/runtime/internal/controllers/extensionconfig_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package controllers

import (
"context"
"fmt"
"strings"

"github.com/pkg/errors"
Expand Down Expand Up @@ -151,6 +152,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
}

// Register the ExtensionConfig if it was found and patched without error.
log.Info("Registering ExtensionConfig information into registry")
if err = r.RuntimeClient.Register(discoveredExtensionConfig); err != nil {
return ctrl.Result{}, errors.Wrapf(err, "failed to register ExtensionConfig %s/%s", extensionConfig.Namespace, extensionConfig.Name)
}
Expand All @@ -175,7 +177,9 @@ func patchExtensionConfig(ctx context.Context, client client.Client, original, m

// reconcileDelete will remove the ExtensionConfig from the registry on deletion of the object. Note this is a best
// effort deletion that may not catch all cases.
func (r *Reconciler) reconcileDelete(_ context.Context, extensionConfig *runtimev1.ExtensionConfig) (ctrl.Result, error) {
func (r *Reconciler) reconcileDelete(ctx context.Context, extensionConfig *runtimev1.ExtensionConfig) (ctrl.Result, error) {
log := ctrl.LoggerFrom(ctx)
log.Info("Unregistering ExtensionConfig information from registry")
if err := r.RuntimeClient.Unregister(extensionConfig); err != nil {
return ctrl.Result{}, errors.Wrapf(err, "failed to unregister %s", tlog.KObj{Obj: extensionConfig})
}
Expand Down Expand Up @@ -224,12 +228,16 @@ func discoverExtensionConfig(ctx context.Context, runtimeClient runtimeclient.Cl
// Note: This was implemented to behave similar to the cert-manager cainjector.
// We couldn't use the cert-manager cainjector because it doesn't work with CustomResources.
func reconcileCABundle(ctx context.Context, client client.Client, config *runtimev1.ExtensionConfig) error {
log := ctrl.LoggerFrom(ctx)

secretNameRaw, ok := config.Annotations[runtimev1.InjectCAFromSecretAnnotation]
if !ok {
return nil
}
secretName := splitNamespacedName(secretNameRaw)

log.Info(fmt.Sprintf("Injecting CA Bundle into ExtensionConfig from secret %q", secretNameRaw))

if secretName.Namespace == "" || secretName.Name == "" {
return errors.Errorf("failed to reconcile caBundle: secret name %q must be in the form <namespace>/<name>", secretNameRaw)
}
Expand Down
8 changes: 8 additions & 0 deletions exp/runtime/internal/controllers/warmup.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/pkg/errors"
kerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/klog/v2"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/manager"
Expand Down Expand Up @@ -89,6 +90,8 @@ func (r *warmupRunnable) Start(ctx context.Context) error {
// warmupRegistry attempts to discover all existing ExtensionConfigs and patch their status with discovered Handlers.
// It warms up the registry by passing it the up-to-date list of ExtensionConfigs.
func warmupRegistry(ctx context.Context, client client.Client, reader client.Reader, runtimeClient runtimeclient.Client) error {
log := ctrl.LoggerFrom(ctx)

var errs []error

extensionConfigList := runtimev1.ExtensionConfigList{}
Expand All @@ -100,6 +103,9 @@ func warmupRegistry(ctx context.Context, client client.Client, reader client.Rea
extensionConfig := &extensionConfigList.Items[i]
original := extensionConfig.DeepCopy()

log := log.WithValues("extensionConfig", klog.KObj(extensionConfig), "name", extensionConfig.Name, "namespace", extensionConfig.Namespace)
ctx := ctrl.LoggerInto(ctx, log)

// Inject CABundle from secret if annotation is set. Otherwise https calls may fail.
if err := reconcileCABundle(ctx, client, extensionConfig); err != nil {
errs = append(errs, err)
Expand Down Expand Up @@ -128,5 +134,7 @@ func warmupRegistry(ctx context.Context, client client.Client, reader client.Rea
return err
}

log.Info("The extension registry is warmed up")

return nil
}
6 changes: 6 additions & 0 deletions internal/controllers/topology/cluster/cluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@ import (
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
"sigs.k8s.io/cluster-api/api/v1beta1/index"
"sigs.k8s.io/cluster-api/controllers/external"
runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog"
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
"sigs.k8s.io/cluster-api/feature"
"sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/patches"
"sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/scope"
"sigs.k8s.io/cluster-api/internal/controllers/topology/cluster/structuredmerge"
"sigs.k8s.io/cluster-api/internal/hooks"
tlog "sigs.k8s.io/cluster-api/internal/log"
runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client"
"sigs.k8s.io/cluster-api/util"
"sigs.k8s.io/cluster-api/util/annotations"
Expand Down Expand Up @@ -273,6 +275,7 @@ func (r *Reconciler) setupDynamicWatches(ctx context.Context, s *scope.Scope) er
func (r *Reconciler) callBeforeClusterCreateHook(ctx context.Context, s *scope.Scope) (reconcile.Result, error) {
// If the cluster objects (InfraCluster, ControlPlane, etc) are not yet created we are in the creation phase.
// Call the BeforeClusterCreate hook before proceeding.
log := tlog.LoggerFrom(ctx)
if s.Current.Cluster.Spec.InfrastructureRef == nil && s.Current.Cluster.Spec.ControlPlaneRef == nil {
hookRequest := &runtimehooksv1.BeforeClusterCreateRequest{
Cluster: *s.Current.Cluster,
Expand All @@ -283,6 +286,7 @@ func (r *Reconciler) callBeforeClusterCreateHook(ctx context.Context, s *scope.S
}
s.HookResponseTracker.Add(runtimehooksv1.BeforeClusterCreate, hookResponse)
if hookResponse.RetryAfterSeconds != 0 {
log.Infof("Creation of Cluster topology is blocked by %s hook", runtimecatalog.HookName(runtimehooksv1.BeforeClusterCreate))
return ctrl.Result{RequeueAfter: time.Duration(hookResponse.RetryAfterSeconds) * time.Second}, nil
}
}
Expand Down Expand Up @@ -338,6 +342,7 @@ func (r *Reconciler) machineDeploymentToCluster(o client.Object) []ctrl.Request
func (r *Reconciler) reconcileDelete(ctx context.Context, cluster *clusterv1.Cluster) (ctrl.Result, error) {
// Call the BeforeClusterDelete hook if the 'ok-to-delete' annotation is not set
// and add the annotation to the cluster after receiving a successful non-blocking response.
log := tlog.LoggerFrom(ctx)
if feature.Gates.Enabled(feature.RuntimeSDK) {
if !hooks.IsOkToDelete(cluster) {
hookRequest := &runtimehooksv1.BeforeClusterDeleteRequest{
Expand All @@ -348,6 +353,7 @@ func (r *Reconciler) reconcileDelete(ctx context.Context, cluster *clusterv1.Clu
return ctrl.Result{}, err
}
if hookResponse.RetryAfterSeconds != 0 {
log.Infof("Cluster deletion is blocked by %q hook", runtimecatalog.HookName(runtimehooksv1.BeforeClusterDelete))
return ctrl.Result{RequeueAfter: time.Duration(hookResponse.RetryAfterSeconds) * time.Second}, nil
}
// The BeforeClusterDelete hook returned a non-blocking response. Now the cluster is ready to be deleted.
Expand Down
4 changes: 4 additions & 0 deletions internal/controllers/topology/cluster/desired_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (

clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
"sigs.k8s.io/cluster-api/controllers/external"
runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog"
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
"sigs.k8s.io/cluster-api/feature"
"sigs.k8s.io/cluster-api/internal/contract"
Expand Down Expand Up @@ -257,6 +258,7 @@ func (r *Reconciler) computeControlPlane(ctx context.Context, s *scope.Scope, in
// The version is calculated using the state of the current machine deployments, the current control plane
// and the version defined in the topology.
func (r *Reconciler) computeControlPlaneVersion(ctx context.Context, s *scope.Scope) (string, error) {
log := tlog.LoggerFrom(ctx)
desiredVersion := s.Blueprint.Topology.Version
// If we are creating the control plane object (current control plane is nil), use version from topology.
if s.Current.ControlPlane == nil || s.Current.ControlPlane.Object == nil {
Expand Down Expand Up @@ -334,6 +336,7 @@ func (r *Reconciler) computeControlPlaneVersion(ctx context.Context, s *scope.Sc
// change the UpgradeTracker accordingly, otherwise the hook call is completed and we
// can remove this hook from the list of pending-hooks.
if hookResponse.RetryAfterSeconds != 0 {
log.Infof("MachineDeployments upgrade to version %q are blocked by %q hook", desiredVersion, runtimecatalog.HookName(runtimehooksv1.AfterControlPlaneUpgrade))
s.UpgradeTracker.MachineDeployments.HoldUpgrades(true)
} else {
if err := hooks.MarkAsDone(ctx, r.Client, s.Current.Cluster, runtimehooksv1.AfterControlPlaneUpgrade); err != nil {
Expand Down Expand Up @@ -383,6 +386,7 @@ func (r *Reconciler) computeControlPlaneVersion(ctx context.Context, s *scope.Sc
s.HookResponseTracker.Add(runtimehooksv1.BeforeClusterUpgrade, hookResponse)
if hookResponse.RetryAfterSeconds != 0 {
// Cannot pickup the new version right now. Need to try again later.
log.Infof("Cluster upgrade to version %q is blocked by %q hook", desiredVersion, runtimecatalog.HookName(runtimehooksv1.BeforeClusterUpgrade))
return *currentVersion, nil
}

Expand Down
28 changes: 27 additions & 1 deletion internal/runtime/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
Expand All @@ -40,6 +41,7 @@ import (
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/client-go/transport"
"k8s.io/utils/pointer"
ctrl "sigs.k8s.io/controller-runtime"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"

runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1"
Expand Down Expand Up @@ -117,6 +119,9 @@ func (c *client) IsReady() bool {
}

func (c *client) Discover(ctx context.Context, extensionConfig *runtimev1.ExtensionConfig) (*runtimev1.ExtensionConfig, error) {
log := ctrl.LoggerFrom(ctx)
log.Info("Performing discovery for ExtensionConfig")

hookGVH, err := c.catalog.GroupVersionHook(runtimehooksv1.Discovery)
if err != nil {
return nil, errors.Wrapf(err, "failed to discover extension %q: failed to compute GVH of hook", extensionConfig.Name)
Expand Down Expand Up @@ -187,9 +192,12 @@ func (c *client) Unregister(extensionConfig *runtimev1.ExtensionConfig) error {
// See CallExtension for more details on when an ExtensionHandler returns an error.
// The aggregated result of the ExtensionHandlers is updated into the response object passed to the function.
func (c *client) CallAllExtensions(ctx context.Context, hook runtimecatalog.Hook, forObject metav1.Object, request runtime.Object, response runtimehooksv1.ResponseObject) error {
hookName := runtimecatalog.HookName(hook)
log := ctrl.LoggerFrom(ctx).WithValues("hook", hookName)
ctx = ctrl.LoggerInto(ctx, log)
gvh, err := c.catalog.GroupVersionHook(hook)
if err != nil {
return errors.Wrapf(err, "failed to call extension handlers for hook %q: failed to compute GroupVersionHook", runtimecatalog.HookName(hook))
return errors.Wrapf(err, "failed to call extension handlers for hook %q: failed to compute GroupVersionHook", hookName)
}
// Make sure the request is compatible with the hook.
if err := c.catalog.ValidateRequest(gvh, request); err != nil {
Expand All @@ -205,6 +213,7 @@ func (c *client) CallAllExtensions(ctx context.Context, hook runtimecatalog.Hook
return errors.Wrapf(err, "failed to call extension handlers for hook %q", gvh.GroupHook())
}

log.Info(fmt.Sprintf("Calling all extensions of hook %q", hookName))
responses := []runtimehooksv1.ResponseObject{}
for _, registration := range registrations {
// Creates a new instance of the response parameter.
Expand All @@ -221,12 +230,14 @@ func (c *client) CallAllExtensions(ctx context.Context, hook runtimecatalog.Hook
}
// If the object namespace isn't matched by the registration NamespaceSelector skip the call.
if !namespaceMatches {
log.V(5).Info(fmt.Sprintf("skipping extension handler %q as object '%s/%s' does not match selector %q of ExtensionConfig", registration.Name, forObject.GetNamespace(), forObject.GetName(), registration.NamespaceSelector))
continue
}

err = c.CallExtension(ctx, hook, forObject, registration.Name, request, tmpResponse)
// If one of the extension handlers fails lets short-circuit here and return early.
if err != nil {
log.Error(err, "failed to call extension handlers")
return errors.Wrapf(err, "failed to call extension handlers for hook %q", gvh.GroupHook())
}
responses = append(responses, tmpResponse)
Expand Down Expand Up @@ -289,6 +300,8 @@ func lowestNonZeroRetryAfterSeconds(i, j int32) int32 {
// - Internal errors. Examples: hooks is incompatible with ExtensionHandler, ExtensionHandler information is missing.
// - Error when ExtensionHandler returns a response with `Status` set to `Failure`.
func (c *client) CallExtension(ctx context.Context, hook runtimecatalog.Hook, forObject metav1.Object, name string, request runtime.Object, response runtimehooksv1.ResponseObject) error {
log := ctrl.LoggerFrom(ctx).WithValues("extensionHandler", name, "hook", runtimecatalog.HookName(hook))
ctx = ctrl.LoggerInto(ctx, log)
hookGVH, err := c.catalog.GroupVersionHook(hook)
if err != nil {
return errors.Wrapf(err, "failed to call extension handler %q: failed to compute GroupVersionHook", name)
Expand Down Expand Up @@ -320,6 +333,7 @@ func (c *client) CallExtension(ctx context.Context, hook runtimecatalog.Hook, fo
return errors.Errorf("failed to call extension handler %q: namespaceSelector did not match object %s", name, util.ObjectKey(forObject))
}

log.Info(fmt.Sprintf("Calling extension handler %q", name))
var timeoutDuration time.Duration
if registration.TimeoutSeconds != nil {
timeoutDuration = time.Duration(*registration.TimeoutSeconds) * time.Second
Expand All @@ -339,18 +353,27 @@ func (c *client) CallExtension(ctx context.Context, hook runtimecatalog.Hook, fo
ignore := *registration.FailurePolicy == runtimev1.FailurePolicyIgnore
if _, ok := err.(errCallingExtensionHandler); ok && ignore {
// Update the response to a default success response and return.
log.Info(fmt.Sprintf("ignoring error calling extension handler because of FailurePolicy %q", *registration.FailurePolicy))
response.SetStatus(runtimehooksv1.ResponseStatusSuccess)
response.SetMessage("")
return nil
}
log.Error(err, "failed to call extension handler")
return errors.Wrapf(err, "failed to call extension handler %q", name)
}

// If the received response is a failure then return an error.
if response.GetStatus() == runtimehooksv1.ResponseStatusFailure {
log.Error(err, "extension handler returned a failure response")
return errors.Errorf("failed to call extension handler %q: got failure response with message %q", name, response.GetMessage())
}

if retryResponse, ok := response.(runtimehooksv1.RetryResponseObject); ok && retryResponse.GetRetryAfterSeconds() != 0 {
log.Info(fmt.Sprintf("extension handler returned blocking response with retryAfterSeconds of %q", retryResponse.GetRetryAfterSeconds()))
} else {
log.Info("extension handler returned success response")
}

// Received a successful response from the extension handler. The `response` object
// has been populated with the result. Return no error.
return nil
Expand All @@ -366,6 +389,7 @@ type httpCallOptions struct {
}

func httpCall(ctx context.Context, request, response runtime.Object, opts *httpCallOptions) error {
log := ctrl.LoggerFrom(ctx)
if opts == nil || request == nil || response == nil {
return errors.New("http call failed: opts, request and response cannot be nil")
}
Expand All @@ -389,6 +413,7 @@ func httpCall(ctx context.Context, request, response runtime.Object, opts *httpC
responseLocal := response

if requireConversion {
log.V(5).Info(fmt.Sprintf("Hook version of supported request is %s. Converting request from %s", opts.registrationGVH, opts.hookGVH))
// The request and response objects need to be converted to match the version supported by
// the ExtensionHandler.
var err error
Expand Down Expand Up @@ -485,6 +510,7 @@ func httpCall(ctx context.Context, request, response runtime.Object, opts *httpC
}

if requireConversion {
log.V(5).Info(fmt.Sprintf("Hook version of received response is %s. Converting response to %s", opts.registrationGVH, opts.hookGVH))
// Convert the received response to the original version of the response object.
if err := opts.catalog.Convert(responseLocal, response, ctx); err != nil {
return errors.Wrapf(err, "http call failed: failed to convert response from %T to %T", requestLocal, response)
Expand Down

0 comments on commit 6df168f

Please sign in to comment.