sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/services/resourcehealth/resourcehealth.go (about)

     1  /*
     2  Copyright 2022 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package resourcehealth
    18  
    19  import (
    20  	"context"
    21  
    22  	"github.com/pkg/errors"
    23  	corev1 "k8s.io/api/core/v1"
    24  	infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
    25  	"sigs.k8s.io/cluster-api-provider-azure/azure"
    26  	"sigs.k8s.io/cluster-api-provider-azure/azure/converters"
    27  	"sigs.k8s.io/cluster-api-provider-azure/feature"
    28  	"sigs.k8s.io/cluster-api-provider-azure/util/tele"
    29  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    30  	"sigs.k8s.io/cluster-api/util/conditions"
    31  )
    32  
    33  const serviceName = "resourcehealth"
    34  
    35  // ResourceHealthScope defines the scope interface for a resourcehealth service.
    36  type ResourceHealthScope interface {
    37  	azure.Authorizer
    38  	AvailabilityStatusResourceURI() string
    39  	AvailabilityStatusResource() conditions.Setter
    40  }
    41  
    42  // AvailabilityStatusFilterer transforms the condition derived from the
    43  // availability status to allow the condition to be overridden in specific
    44  // circumstances.
    45  type AvailabilityStatusFilterer interface {
    46  	AvailabilityStatusFilter(cond *clusterv1.Condition) *clusterv1.Condition
    47  }
    48  
    49  // Service provides operations on Azure resources.
    50  type Service struct {
    51  	Scope ResourceHealthScope
    52  	client
    53  }
    54  
    55  // New creates a new service.
    56  func New(scope ResourceHealthScope) (*Service, error) {
    57  	cli, err := newClient(scope)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	return &Service{
    62  		Scope:  scope,
    63  		client: cli,
    64  	}, nil
    65  }
    66  
    67  // Name returns the service name.
    68  func (s *Service) Name() string {
    69  	return serviceName
    70  }
    71  
    72  // Reconcile ensures the resource's availability status is reflected in its own status.
    73  func (s *Service) Reconcile(ctx context.Context) error {
    74  	ctx, log, done := tele.StartSpanWithLogger(ctx, "resourcehealth.Service.Reconcile")
    75  	defer done()
    76  
    77  	if !feature.Gates.Enabled(feature.AKSResourceHealth) {
    78  		conditions.Delete(s.Scope.AvailabilityStatusResource(), infrav1.AzureResourceAvailableCondition)
    79  		return nil
    80  	}
    81  
    82  	resource := s.Scope.AvailabilityStatusResourceURI()
    83  	availStatus, err := s.GetByResource(ctx, resource)
    84  	if err != nil {
    85  		return errors.Wrapf(err, "failed to get availability status for resource %s", resource)
    86  	}
    87  	log.V(2).Info("got availability status for resource", "resource", resource, "status", availStatus)
    88  
    89  	cond := converters.SDKAvailabilityStatusToCondition(availStatus)
    90  	if filterer, ok := s.Scope.(AvailabilityStatusFilterer); ok {
    91  		cond = filterer.AvailabilityStatusFilter(cond)
    92  	}
    93  
    94  	conditions.Set(s.Scope.AvailabilityStatusResource(), cond)
    95  
    96  	if cond.Status == corev1.ConditionFalse {
    97  		return errors.Errorf("resource is not available: %s", cond.Message)
    98  	}
    99  
   100  	return nil
   101  }
   102  
   103  // Delete is a no-op.
   104  func (s *Service) Delete(ctx context.Context) error {
   105  	_, _, done := tele.StartSpanWithLogger(ctx, "resourcehealth.Service.Delete")
   106  	defer done()
   107  
   108  	return nil
   109  }
   110  
   111  // IsManaged always returns true.
   112  func (s *Service) IsManaged(ctx context.Context) (bool, error) {
   113  	return true, nil
   114  }