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 }