sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/services/vmextensions/vmextensions.go (about) 1 /* 2 Copyright 2021 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 vmextensions 18 19 import ( 20 "context" 21 22 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5" 23 "github.com/pkg/errors" 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/services/async" 27 "sigs.k8s.io/cluster-api-provider-azure/util/tele" 28 ) 29 30 const serviceName = "vmextensions" 31 32 // VMExtensionScope defines the scope interface for a vm extension service. 33 type VMExtensionScope interface { 34 azure.Authorizer 35 azure.AsyncStatusUpdater 36 VMExtensionSpecs() []azure.ResourceSpecGetter 37 } 38 39 // Service provides operations on Azure resources. 40 type Service struct { 41 Scope VMExtensionScope 42 async.Reconciler 43 } 44 45 // New creates a new vm extension service. 46 func New(scope VMExtensionScope) (*Service, error) { 47 client, err := newClient(scope, scope.DefaultedAzureCallTimeout()) 48 if err != nil { 49 return nil, err 50 } 51 return &Service{ 52 Scope: scope, 53 Reconciler: async.New[armcompute.VirtualMachineExtensionsClientCreateOrUpdateResponse, 54 armcompute.VirtualMachineExtensionsClientDeleteResponse](scope, client, client), 55 }, nil 56 } 57 58 // Name returns the service name. 59 func (s *Service) Name() string { 60 return serviceName 61 } 62 63 // Reconcile idempotently creates or updates a VM extension. 64 func (s *Service) Reconcile(ctx context.Context) error { 65 ctx, _, done := tele.StartSpanWithLogger(ctx, "vmextensions.Service.Reconcile") 66 defer done() 67 68 ctx, cancel := context.WithTimeout(ctx, s.Scope.DefaultedAzureServiceReconcileTimeout()) 69 defer cancel() 70 71 specs := s.Scope.VMExtensionSpecs() 72 if len(specs) == 0 { 73 return nil 74 } 75 76 // We go through the list of ExtensionSpecs to reconcile each one, independently of the result of the previous one. 77 // If multiple errors occur, we return the most pressing one. 78 // Order of precedence (highest -> lowest) is: error that is not an operationNotDoneError (i.e. error creating) -> operationNotDoneError (i.e. creating in progress) -> no error (i.e. created) 79 var resultErr error 80 for _, extensionSpec := range specs { 81 _, err := s.CreateOrUpdateResource(ctx, extensionSpec, serviceName) 82 if err != nil { 83 if !azure.IsOperationNotDoneError(err) || resultErr == nil { 84 resultErr = err 85 } 86 } 87 } 88 89 if azure.IsOperationNotDoneError(resultErr) { 90 resultErr = errors.Wrapf(resultErr, "extension is still in provisioning state. This likely means that bootstrapping has not yet completed on the VM") 91 } else if resultErr != nil { 92 resultErr = errors.Wrapf(resultErr, "extension state failed. This likely means the Kubernetes node bootstrapping process failed or timed out. Check VM boot diagnostics logs to learn more") 93 } 94 95 s.Scope.UpdatePutStatus(infrav1.BootstrapSucceededCondition, serviceName, resultErr) 96 return resultErr 97 } 98 99 // Delete is a no-op. VM Extensions will be deleted as part of VM deletion. 100 func (s *Service) Delete(_ context.Context) error { 101 return nil 102 } 103 104 // IsManaged returns always returns true as CAPZ does not support BYO VM extension. 105 func (s *Service) IsManaged(ctx context.Context) (bool, error) { 106 return true, nil 107 }